Android之Bluetooth

對於Android的英文幫助文檔,總是看了記不住,遠不如對中文資料那麼印象深,所以下面的敘述都是對Android幫助文檔Bluetooth的翻譯。

一、Bluetooth
Android平臺包含了對Bluetooth協議棧的支持,允許機器通過Bluetooth設備進行無線數據交換。應用框架通過Android Bluetooth API訪問Bluetooth功能模塊。 這些API能讓應用無線連接其他Bluetooth設備,實現點對點和多點之間的通信。
運用藍牙API,Android應用程序可以完成如下操作:
1、掃描其他Bluetooth設備。
2、查詢配對Bluetooth設備的本地Bluetooth適配器。
3、建立RFCOMM通道。
4、通過服務探索連接到其他設備。
5、與其他設備進行數據傳輸。
6、管理多個連接

二、The Basics
本文描述如何使用Android Bluetooth APIs完成Bluetooth通訊的4個必要任務:設置Bluetooth,搜尋本地配對或者可用的Bluetooth設備,連接Bluetooth設備,與Bluetooth設備進行數據傳輸。
所有可用的Bluetooth APIs都包含在android.bluetooth包中。下面是建立Bluetooth連接需要用到的類和接口的總結:
1、BluetoothAdapter
描述本地Bluetooth適配器(Bluetooth接收器)。BluetoothAdapter是所有Bluetooth相關活動的入口。運用 BluetoothAdapter可以發現其他Bluetooth設備,查詢連接(或配對)的設備列表,用已知MAC地址實例化一個 BluetoothDevice對象,創建一個BluetoothServerSocket對象偵聽其他設備的通信。
2、BluetoothDevice
描述一個遠程Bluetooth設備。可以用它通過一個BluetoothSocket請求一個遠程設備的連接,或者查詢遠程設備的名稱、地址、類、連接狀態等信息。
3、BluetoothSocket
描述一個Bluetooth Socket接口(類似於TCP Socket)。應用通過InputStream和OutputStream.與另外一個Bluetooth設備交換數據,即它是應用與另外一個設備交換數據的連接點。
4、BluetoothServerSocket
描述一個開放的socket服務器,用來偵聽連接進來的請求(類似於RCP ServerSocket)。爲了連接兩個Android設備,一個設備必須使用該類來開啓一個socket做服務器,當另外一個設備對它發起連接請求時 並且請求被接受時,BluetoothServerSocket會返回一個連接的BluetoothSocket對象。
5、BluetoothClass
描述一個Bluetooth設備的一般規格和功能。這個是用來定義設備類和它的服務的只讀屬性集。然而,它並不是可靠的描述設備支持的所有Bluetooth配置和服務,而只是一些設備類型的有用特徵。
6、BluetoothProfile
描述Bluetooth Profile的接口。Bluetooth Profile是兩個設備基於藍牙通訊的無線接口描述。
(對Bluetooth Profile的詳細解釋,來自百度:爲了更容易的保持Bluetooth設備之間的兼容,Bluetooth規範中定義了 Profile。Profile定義了設備如何實現一種連接或者應用,你可以把Profile理解爲連接層或者應用層協議。 比如,如果一家公司希望它們的Bluetooth芯片支援所有的Bluetooth耳機,那麼它只要支持HeadSet Profile即可,而無須考慮該芯片與其它Bluetooth設備的通訊與兼容性問題。如果你想購買Bluetooth產品,你應該瞭解你的應用需要哪 些Profile來完成,並且確保你購買的Bluetooth產品支持這些Profile。)
7、BluetoothHeadset
提供移動電話的Bluetooth耳機支持。包括Bluetooth耳機和Hands-Free (v1.5) profiles。
8、BluetoothA2dp
定義兩個設備間如何通過Bluetooth連接進行高質量的音頻傳輸。
A2DP(Advanced Audio Distribution Profile):高級音頻傳輸模式。
9、BluetoothProfile.ServiceListener
一個接口描述,在與服務連接或者斷連接的時候通知BluetoothProfile IPC(這是內部服務運行的一個特定的模式<profile>)。

三、Bluetooth Permissions
要使用Bluetooth功能,至少需要2個Bluetooth權限:BLUETOOTHBLUETOOTH_ADMIN.
BLUETOOTH:用來授權任何Bluetooth通信,如請求連接,接受連接,傳輸數據等。
BLUETOOTH_ADMIN:用來授權初始化設備搜索或操作Bluetooth設置。大多數應用需要它的唯一場合是用來搜索本地Bluetooth設 備。本授權的其他功能不應該被使用,除非是需要修改Bluetooth設置的“power manager(電源管理)”應用。
注意:需要BLUETOOTH_ADMIN權限的場合,BLUETOOTH權限也是必需的。
需要在manifest文件中聲明Bluetooth權限,示例如下:
<manifest ... > 
  <uses-permission android:name="android.permission.BLUETOOTH" /> 
  ... 
</manifest>

四、Setting Up Bluetooth
在用Bluetooth通訊之前,需要確認設備是否支持Bluetooth,如果支持,還得確保Bluetooth是可用的。
如果設備不支持Bluetooth,需要優雅的將Bluetooth置爲不可用。如果支持Bluetooth,但沒有開啓,可以在應用中請求開啓Bluetooth。該設置使用BluetoothAdapter.通過兩個步驟完成。
1、獲取BluetoothAdapter
BluetoothAdapter是每個Bluetooth的Activity都需要用到的。用靜態方法getDefaultAdapter()獲取 BluetoothAdapter,返回一個擁有Bluetooth 適配器的BluetoothAdapter對象。如果返回null,說明設備不支持Bluetooth,關於Bluetooth的故事到此就結束了(因爲 你幹不了什麼了)。示例:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 
if (mBluetoothAdapter == null) { 
    // Device does not support Bluetooth 
}
2、Enable Bluetooth
接下來,就是確保Bluetooth功能是開啓的。調用isEnabled()來檢查Bluetooth當前是否是開啓的。用 ACTION_REQUEST_ENABLE action Intent調用startActivityForResult()來請求開啓Bluetooth,這會通過系統設置發出一個Bluetooth使能請求 (並且不會停止本應用程序)。示例:
if (!mBluetoothAdapter.isEnabled()) { 
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); 

用戶請求使能Bluetooth時,會顯示一個對話框。選擇“Yes”,系統會使能Bluetooth,並且焦點會返回你的應用程序。
如果使能Bluetooth成功,你的Activity會在onActivityResult()回調函數中收到RESULT_OK的結果碼。如果Bluetooth使能因發生錯誤(或用戶選擇了“No”)而失敗,收到的結果碼將是RESULT_CANCELED。
作爲可選項,應用也可以偵聽ACTION_STATE_CHANGED broadcast Intent,這樣無論Bluetooth狀態何時被改變系統都會發出broadcast(廣播)。該廣播包含附加的字段信息EXTRA_STATE和 EXTRA_PREVIOUS_STATE分別代表新的和舊的Bluetooth狀態,該字段可能的值爲STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, 和STATE_OFF。應用運行時,偵聽ACTION_STATE_CHANGED廣播來檢測Bluetooth狀態的改變是很有用的。
提示:啓用Bluetooth可被發現功能能夠自動開啓Bluetooth。如果在完成Activity之前需要持續的使能Bluetooth可被發現功能,那麼上面的第2步就可以忽略。

五、Finding Devices
使用BluetoothAdapter可以通過設備搜索或查詢配對設備找到遠程Bluetooth設備。
Device discovery(設備搜索)是一個掃描搜索本地已使能Bluetooth設備並且從搜索到的設備請求一些信息的過程(有時候會收到類似 “discovering”,“inquiring”或“scanning”)。但是,搜索到的本地Bluetooth設備只有在打開被發現功能後纔會響 應一個discovery請求,響應的信息包括設備名,類,唯一的MAC地址。發起搜尋的設備可以使用這些信息來初始化跟被發現的設備的連接。
一旦與遠程設備的第一次連接被建立,一個pairing請求就會自動提交給用戶。如果設備已配對,配對設備的基本信息(名稱,類,MAC地址)就被保存下 來了,能夠使用Bluetooth API來讀取這些信息。使用已知的遠程設備的MAC地址,連接可以在任何時候初始化而不必先完成搜索(當然這是假設遠程設備是在可連接的空間範圍內)。

需要記住,配對和連接是兩個不同的概念
配對意思是兩個設備相互意識到對方的存在,共享一個用來鑑別身份的鏈路鍵(link-key),能夠與對方建立一個加密的連接。
連接意思是兩個設備現在共享一個RFCOMM信道,能夠相互傳輸數據。

目前Android Bluetooth API's要求設備在建立RFCOMM信道前必須配對(配對是在使用Bluetooth API初始化一個加密連接時自動完成的)。

下面描述如何查詢已配對設備,搜索新設備。
注意:Android的電源設備默認是不能被發現的。用戶可以通過系統設置讓它在有限的時間內可以被發現,或者可以在應用程序中要求用戶使能被發現功能。

1、Querying paired devices
在搜索設備前,查詢配對設備看需要的設備是否已經是已經存在是很值得的,可以調用getBondedDevices()來做到,該函數會返回一個描述配對 設備BluetoothDevice的結果集。例如,可以使用ArrayAdapter查詢所有配對設備然後顯示所有設備名給用戶:
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); 
// If there are paired devices 
if (pairedDevices.size() > 0) { 
    // Loop through paired devices 
    for (BluetoothDevice device : pairedDevices) { 
        // Add the name and address to an array adapter to show in a ListView 
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress()); 
    } 
}
BluetoothDevice對象中需要用來初始化一個連接唯一需要用到的信息就是MAC地址。

2、Discovering devices
要開始搜索設備,只需簡單的調用startDiscovery()。該函數時異步的,調用後立即返回,返回值表示搜索是否成功開始。搜索處理通常包括一個12秒鐘的查詢掃描,然後跟隨一個頁面顯示搜索到設備Bluetooth名稱。
應用中可以註冊一個帶CTION_FOUND Intent的BroadcastReceiver,搜索到每一個設備時都接收到消息。對於每一個設備,系統都會廣播ACTION_FOUND Intent,該Intent攜帶着而外的字段信息EXTRA_DEVICE和EXTRA_CLASS,分別包含一個BluetoothDevice和一 個BluetoothClass。下面的示例顯示如何註冊和處理設備被發現後發出的廣播:
// Create a BroadcastReceiver for ACTION_FOUND 
private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 
    public void onReceive(Context context, Intent intent) { 
        String action = intent.getAction(); 
        // When discovery finds a device 
        if (BluetoothDevice.ACTION_FOUND.equals(action)) { 
            // Get the BluetoothDevice object from the Intent 
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress()); 
        } 
    } 
}; 
// Register the BroadcastReceiver 
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

警告:完成設備搜索對於Bluetooth適配器來說是一個重量級的處理,要消耗大量它的資源。一旦你已經找到一個設備來連接,請確保你在嘗試連接 前使用了cancelDiscovery()來停止搜索。同樣,如果已經保持了一個連接的時候,同時執行搜索設備將會顯著的降低連接的帶寬,所以在連接的 時候不應該執行搜索發現。

3、Enabling discoverability
如果想讓本地設備被其他設備發現,可以帶ACTION_REQUEST_DISCOVERABLE action Intent調用startActivityForResult(Intent, int) 方法。該方法會提交一個請求通過系統剛設置使設備出於可以被發現的模式(而不影響應用程序)。默認情況下,設備在120秒後變爲可以被發現的。可以通過額 外增加EXTRA_DISCOVERABLE_DURATION Intent自定義一個值,最大值是3600秒,0表示設備總是可以被發現的(小於0或者大於3600則會被自動設置爲120秒)。下面示例設置時間爲 300:
Intent discoverableIntent = new 
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); 
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); 
startActivity(discoverableIntent);
詢問用戶是否允許打開設備可以被發現功能時會顯示一個對話框。如果用戶選擇“Yes”,設備會在指定時間過後變爲可以被發現的。Activity的 onActivityResult()回調函數被調用,結果碼等於設備變爲可以被發現所需時長。如果用戶選擇“No”或者有錯誤發生,結果碼會是 Activity.RESULT_CANCELLED。
提示:如果Bluetooth沒有啓用,啓用Bluetooth可被發現功能能夠自動開啓Bluetooth。
在規定的時間內,設備會靜靜的保持可以被發現模式。如果想在可以被發現模式被更改時受到通知,可以用ACTION_SCAN_MODE_CHANGED Intent註冊一個BroadcastReceiver,包含額外的字段信息EXTRA_SCAN_MODE和 EXTRA_PREVIOUS_SCAN_MODE分別表示新舊掃描模式,其可能的值爲 SCAN_MODE_CONNECTABLE_DISCOVERABLE(discoverable mode),SCAN_MODE_CONNECTABLE(not in discoverable mode but still able to receive connections),SCAN_MODE_NONE(not in discoverable mode and unable to receive connections)。
如果只需要連接遠程設備就不需要打開設備的可以被發現功能。只在應用作爲一個服務器socket的宿主用來接收進來的連接時才需要使能可以被發現功能,因爲遠程設備在初始化連接前必須先發現了你的設備。

六、Connecting Devices
爲了建立兩個設備之間的應用的連接,需要完成服務器端和客戶端,因爲一個設備必須打開一個服務器socket而另外一個設備必須初始化連接(用服務器端的 MAC地址)。服務器和客戶端在各自獲得一個基於同一個RFCOMM信道的已連接的BluetoothSocket對象後就被認爲連接已經建立。這個時 候,雙方設備可以獲取輸入輸出流,數據傳輸可以開始了。本節描述如何在兩個設備之間初始化連接。
服務器設備和客戶端設備用不同的方式獲取各自需要的BluetoothSocket對象。服務器端的在接收一個進來的連接時獲取到,客戶端的在打開一個與服務器端的RFCOMM信道的時候獲取到。
一個實現技巧是自動把每個設備作爲服務器,這樣就擁有了一個打開的socket用來偵聽連接。然後任一設備就能夠發起與另一個設備的連接,併成爲客戶端。另外,一個設備也可以明確的成爲“host”,並打開一個服務端socket,另一個設備可以簡單的發起連接。
注意:如果兩個設備之前沒有配對,那麼在連接處理過程中Android應用框架會自動顯示一個配對請求的通知或對話框給用戶。因此,當嘗試連接設備時,應 用不需要關心設備是否已經配對。RFCOMM連接會阻塞直到用戶成功將設備配對(如果用戶拒絕配對或者配對超時了連接會失敗)。

1、Connecting as a server
如果要連接兩個設備,其中一個必須充當服務器,通過持有一個打開的BluetoothServerSocket對象。服務器socket的作用是偵聽進來 的連接,如果一個連接被接受,提供一個連接好的BluetoothSocket對象。從BluetoothServerSocket獲取到 BluetoothSocket對象之後,BluetoothServerSocket就可以(也應該)丟棄了,除非你還要用它來接收更多的連接。

下面是建立服務器socket和接收一個連接的基本步驟:
1.通過調用listenUsingRfcommWithServiceRecord(String, UUID)得到一個BluetoothServerSocket對象。
該字符串爲服務的識別名稱,系統將自動寫入到一個新的服務發現協議(SDP)數據庫接入口到設備上的(名字是任意的,可以簡單地是應用程序的名稱)項。 UUID也包括在SDP接入口中,將是客戶端設備連接協議的基礎。也就是說,當客戶端試圖連接本設備,它將攜帶一個UUID用來唯一標識它要連接的服 務,UUID必須匹配,連接纔會被接受。
2.通過調用accept()來偵聽連接請求。
這是一個阻塞的調用,知道有連接進來或者產生異常纔會返回。只有遠程設備發送一個連接請求,並且攜帶的UUID與偵聽它socket註冊的UUID匹配,連接請求才會被接受。如果成功,accept()將返回一個連接好的BluetoothSocket對象。
3.除非需要再接收另外的連接,否則的話調用close()。
close()釋放server socket和它的資源,但不會關閉連接accept()返回的連接好的BluetoothSocket對象。與TCP/IP不同,RFCOMM同一時刻 一個信道只允許一個客戶端連接,因此大多數情況下意味着在BluetoothServerSocket接受一個連接請求後應該立即調用close()。

accept()調用不應該在主Activity UI線程中進行,因爲這是個阻塞的調用,會妨礙其他的交互。經常是在在一個新線程中做BluetoothServerSocket或 BluetoothSocket的所有工作來避免UI線程阻塞。注意所有BluetoothServerSocket或BluetoothSocket的 方法都是線程安全的。

示例:
下面是一個簡單的接受連接的服務器組件代碼示例:
private class AcceptThread extends Thread { 
    private final BluetoothServerSocket mmServerSocket; 
  
    public AcceptThread() { 
        // Use a temporary object that is later assigned to mmServerSocket, 
        // because mmServerSocket is final 
        BluetoothServerSocket tmp = null; 
        try { 
            // MY_UUID is the app's UUID string, also used by the client code 
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { } 
        mmServerSocket = tmp; 
    } 
  
    public void run() { 
        BluetoothSocket socket = null; 
        // Keep listening until exception occurs or a socket is returned 
        while (true) { 
            try { 
                socket = mmServerSocket.accept(); 
            } catch (IOException e) { 
                break; 
            } 
            // If a connection was accepted 
            if (socket != null) { 
                // Do work to manage the connection (in a separate thread) 
                manageConnectedSocket(socket); 
                mmServerSocket.close(); 
                break; 
            } 
        } 
    } 
  
    /** Will cancel the listening socket, and cause the thread to finish */ 
    public void cancel() { 
        try { 
            mmServerSocket.close(); 
        } catch (IOException e) { } 
    } 
}
本例中,僅僅只接受一個進來的連接,一旦連接被接受獲取到BluetoothSocket,就發送獲取到的BluetoothSocket給一個單獨的線程,然後關閉BluetoothServerSocket並跳出循環。
注意:accept()返回BluetoothSocket後,socket已經連接了,所以在客戶端不應該呼叫connnect()。
manageConnectedSocket()是一個虛方法,用來初始化線程好傳輸數據。
通常應該在處理完偵聽到的連接後立即關閉BluetoothServerSocket。在本例中,close()在得到BluetoothSocket後 馬上被調用。還需要在線程中提供一個公共的方法來關閉私有的BluetoothSocket,停止服務端socket的偵聽。

2、Connecting as a client
爲了實現與遠程設備的連接,你必須首先獲得一個代表遠程設備BluetoothDevice對象。然後使用BluetoothDevice對象來獲取一個BluetoothSocket來實現來接。
下面是基本的步驟:
1.用BluetoothDevice調用createRfcommSocketToServiceRecord(UUID)獲取一個BluetoothSocket對象。
這個初始化的BluetoothSocket會連接到BluetoothDevice。UUID必須匹配服務器設備在打開 BluetoothServerSocket 時用到的UUID(用listenUsingRfcommWithServiceRecord(String, UUID))。可以簡單的生成一個UUID串然後在服務器和客戶端都使用該UUID。
2.調用connect()完成連接
當調用這個方法的時候,系統會在遠程設備上完成一個SDP查找來匹配UUID。如果查找成功並且遠程設備接受連接,就共享RFCOMM信道,connect()會返回。這也是一個阻塞的調用,不管連接失敗還是超時(12秒)都會拋出異常。
注意:要確保在調用connect()時沒有同時做設備查找,如果在查找設備,該連接嘗試會顯著的變慢,慢得類似失敗了。

實例:
下面是一個完成Bluetooth連接的樣例線程:
private class ConnectThread extends Thread { 
    private final BluetoothSocket mmSocket; 
    private final BluetoothDevice mmDevice; 
  
    public ConnectThread(BluetoothDevice device) { 
        // Use a temporary object that is later assigned to mmSocket, 
        // because mmSocket is final 
        BluetoothSocket tmp = null; 
        mmDevice = device; 
  
        // Get a BluetoothSocket to connect with the given BluetoothDevice 
        try { 
            // MY_UUID is the app's UUID string, also used by the server code 
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID); 
        } catch (IOException e) { } 
        mmSocket = tmp; 
    } 
  
    public void run() { 
        // Cancel discovery because it will slow down the connection 
        mBluetoothAdapter.cancelDiscovery(); 
  
        try { 
            // Connect the device through the socket. This will block 
            // until it succeeds or throws an exception 
            mmSocket.connect(); 
        } catch (IOException connectException) { 
            // Unable to connect; close the socket and get out 
            try { 
                mmSocket.close(); 
            } catch (IOException closeException) { } 
            return; 
        } 
  
        // Do work to manage the connection (in a separate thread) 
        manageConnectedSocket(mmSocket); 
    } 
  
    /** Will cancel an in-progress connection, and close the socket */ 
    public void cancel() { 
        try { 
            mmSocket.close(); 
        } catch (IOException e) { } 
    } 
}
注意到cancelDiscovery()在連接操作前被調用。在連接之前,不管搜索有沒有進行,該調用都是安全的,不需要確認(當然如果有要確認的需求,可以調用isDiscovering())。
manageConnectedSocket()是一個虛方法,用來初始化線程好傳輸數據。
在對BluetoothSocket的處理完成後,記得調用close()來關閉連接的socket和清理所有的內部資源。

七、Managing a Connection
如果已經連接了兩個設備,他們都已經擁有各自的連接好的BluetoothSocket對象。那就是一個有趣的開始,因爲你可以在設備間共享數據了。使用BluetoothSocket,傳輸任何數據通常來說都很容易了:
1.通過socket獲取輸入輸出流來處理傳輸(分別使用getInputStream()和getOutputStream())。
2.用read(byte[])和write(byte[])來實現讀寫。
僅此而已。

當然,還是有很多細節需要考慮的。首要的,需要用一個專門的線程來實現流的讀寫。只是很重要的,因爲read(byte[])和 write(byte[])都是阻塞的調用。read(byte[])會阻塞直到流中有數據可讀。write(byte[])通常不會阻塞,但是如果遠程 設備調用read(byte[])不夠快導致中間緩衝區滿,它也可能阻塞。所以線程中的主循環應該用於讀取InputStream。線程中也應該有單獨的 方法用來完成寫OutputStream。

示例:
下面是一個如上面描述那樣的例子:
private class ConnectedThread extends Thread { 
    private final BluetoothSocket mmSocket; 
    private final InputStream mmInStream; 
    private final OutputStream mmOutStream; 
  
    public ConnectedThread(BluetoothSocket socket) { 
        mmSocket = socket; 
        InputStream tmpIn = null; 
        OutputStream tmpOut = null; 
  
        // Get the input and output streams, using temp objects because 
        // member streams are final 
        try { 
            tmpIn = socket.getInputStream(); 
            tmpOut = socket.getOutputStream(); 
        } catch (IOException e) { } 
  
        mmInStream = tmpIn; 
        mmOutStream = tmpOut; 
    } 
  
    public void run() { 
        byte[] buffer = new byte[1024];  // buffer store for the stream 
        int bytes; // bytes returned from read() 
  
        // Keep listening to the InputStream until an exception occurs 
        while (true) { 
            try { 
                // Read from the InputStream 
                bytes = mmInStream.read(buffer); 
                // Send the obtained bytes to the UI Activity 
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer) 
                        .sendToTarget(); 
            } catch (IOException e) { 
                break; 
            } 
        } 
    } 
  
    /* Call this from the main Activity to send data to the remote device */ 
    public void write(byte[] bytes) { 
        try { 
            mmOutStream.write(bytes); 
        } catch (IOException e) { } 
    } 
  
    /* Call this from the main Activity to shutdown the connection */ 
    public void cancel() { 
        try { 
            mmSocket.close(); 
        } catch (IOException e) { } 
    } 
}
構造函數中得到需要的流,一旦執行,線程會等待從InputStream來的數據。當read(byte[])返回從流中讀到的字節後,數據通過父類的成員Handler被送到主Activity,然後繼續等待讀取流中的數據。
向外發送數據只需簡單的調用線程的write()方法。
線程的cancel()方法時很重要的,以便連接可以在任何時候通過關閉BluetoothSocket來終止。它應該總在處理完Bluetooth連接後被調用。

八、Working with Profiles
從Android 3.0開始,Bluetooth API就包含了對Bluetooth profiles的支持。Bluetooth profile是基於藍牙的設備之間通信的無線接口規範。例如Hands-Free profile(免提模式)。如果移動電話要連接一個無線耳機,他們都要支持Hands-Free profile。
你在你的類裏可以完成BluetoothProfile接口來支持某一Bluetooth profiles。Android Bluetooth API完成了下面的Bluetooth profile:
Headset:Headset profile提供了移動電話上的Bluetooth耳機支持。Android提供了BluetoothHeadset類,它是一個協議,用來通過 IPC(interprocess communication)控制Bluetooth Headset Service。BluetoothHeadset既包含Bluetooth Headset profile也包含Hands-Free profile,還包括對AT命令的支持。
A2DP:Advanced Audio Distribution Profile (A2DP) profile,高級音頻傳輸模式。Android提供了BluetoothA2dp類,這是一個通過IPC來控制Bluetooth A2DP的協議。

下面是使用profile的基本步驟:
1.獲取默認的Bluetooth適配器。
2.使用getProfileProxy()來建立一個與profile相關的profile協議對象的連接。在下面的例子中,profile協議對象是BluetoothHeadset的一個實例。
3.設置BluetoothProfile.ServiceListener。該listener通知BluetoothProfile IPC客戶端,當客戶端連接或斷連服務器的時候。
4.在onServiceConnected()內,得到一個profile協議對象的句柄。
5. 一旦擁有了profile協議對象,就可以用它來監控連接的狀態,完成於該profile相關的其他操作。

例如,下面的代碼片段顯示如何連接到一個BluetoothHeadset協議對象,用來控制Headset profile:
BluetoothHeadset mBluetoothHeadset; 
  
// Get the default adapter 
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 
  
// Establish connection to the proxy. 
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
  
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) { 
        if (profile == BluetoothProfile.HEADSET) { 
            mBluetoothHeadset = (BluetoothHeadset) proxy; 
        } 
    } 
    public void onServiceDisconnected(int profile) { 
        if (profile == BluetoothProfile.HEADSET) { 
            mBluetoothHeadset = null; 
        } 
    } 
}; 
  
// ... call functions on mBluetoothHeadset 
  
// Close proxy connection after use. 
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);

1、Vendor-specific AT commands
從Android 3.0開始,應用程序可以註冊偵聽預定義的Vendor-specific AT命令這樣的系統廣播(如Plantronics +XEVENT command)。例如,應用可以接收到一個廣播,該廣播表明連接的設備電量過低,然後通知用戶做好其他需要的操作。創建一個帶 ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent的broadcast receiver來爲耳機處理
Vendor-specific AT commands。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章