BlueTooth Android開發基礎

http://blog.csdn.net/wangjia55/article/details/13505597#



android 平臺提供藍牙網絡協議棧的支持,允許一臺設備與其它設備通過無線交換數據。應用框架通過android 藍牙API提供對藍牙功能的使用。這些API允許應用無線連接到其它的藍牙設備,支持點對點、點對多的無線通信。

通過android API,應用程序可以做到:

  • 掃描其它的藍牙設備
  • 查詢藍牙適配器已經配對的藍牙設備
  • 建立RFCOMM信道(channels)
  • 通過服務發現連接到其它設備
  • 與其它設備間發送或者接收數據
  • 管理多個連接

基礎

本文檔主要描述用android API來實現藍牙通信的四個主要步驟:初始化藍牙設備、找到已配對或者附近可用的藍牙設備、連接設備、設備間傳輸數據。

所有的藍牙API都在android.bluetooth包中,下面是建立藍牙連接主要需要用到的類和接口:

BluetoothAdapter

代表本地藍牙適配器,BluetoothAdapter是所有藍牙交互的入口點。通過該對象可以發現其它藍牙設備,查詢已配對設備,用已知的MAC地址實例化一個BluetoothDevice對象,建立一個BluetoothServerSocket用來監聽來自其它藍牙設備的消息。

BluetoothDevice

代表一個遠程藍牙設備,可以通過一個BluetoothSocket對象向一個遠程設備請求連接,或者查詢藍牙設備的信息如名字、地址、類別(class)、配對狀態。

BluetoothSocket

代表一個藍牙socket接口(類似於TCP Socket)。它是應用程序與其它藍牙設備通過IO流交換數據的連接點。

BluetoothServerSocket

代表一個監聽請求的服務器socket(類似於TCP ServerSocket)。要連接兩臺android設備,必須有一個設備用該類打開一個服務器socket。當一臺遠程藍牙設備向該設備發出連接請求時,連接建立時BluetoothServerSocket會返回一個連接後的BluetoothSocket

BluetoothClass

描述一臺藍牙設備的特點和能力。這是一組定義藍牙主要和次要的類別和服務的只讀的屬性值。雖然它並不能完全可靠地描述該設備支持的配置和服務,但是用來分別設別類型很有用。

BluetoothProfile

描述bluetooth profile的接口。bluetooth profile是設備間藍牙通信的無線接口規範。舉一個例子就是Hands-Free profile。關於profile更多信息可以參見Working with Profiles

BluetoothHeadset

爲手機使用藍牙耳機提供支持。它同時包括Headset 和 Hands-Free (v1.5) profiles。

BluetoothA2dp

定義高品質音頻如何通過藍牙連接由一臺設備傳輸到另外一臺設備。"a2dp"代表Advanced Audio Distribution Profile。

BluetoothHealth

代表一個控制藍牙服務的Health Device Profile proxy。

BluetoothHealthCallback

用於實現BluetoothHealth回調的抽象類。開發者必須繼承這個類並且實現其中的回調方法用來接收應用註冊和藍牙信道狀態的變化。

BluetoothHealthAppConfiguration

代表第三方的Bluetooth Health應用連接到遠程Bluetooth health設備的配置。

BluetoothProfile.ServiceListener

通知BluetoothProfile IPC客戶端與service(the internal service that runs a particular profile)連接或者斷開連接的接口。

藍牙權限

爲了在應用程序中使用藍牙特性,開發者至少需要定義這兩個權限中的一個:BLUETOOTHBLUETOOTH_ADMIN

如果應用需要進行一些藍牙通信比如請求連接、接受連接和傳輸數據,開發者應該請求BLUETOOTH權限。

如果應用需要執行發現設備以及藍牙設定則應該請求BLUETOOTH_ADMIN權限。大部分應用程序爲了發現附近藍牙設備都需要這個權限。該權限所授予的其它功能不應該被使用,除非該應用是基於用戶請求修改藍牙設定。如果你使用BLUETOOTH_ADMIN權限,則一定需要同時請求BLUETOOTH權限。

在應用程序的manifest中可以以如下方式定義藍牙權限:

[java] view plaincopy
  1. <manifest ... >  
  2.   <uses-permission android:name="android.permission.BLUETOOTH" />  
  3.   ...  
  4. </manifest>  

可以查看<uses-permission>文檔獲取更詳細的定義應用權限的說明。

初始化藍牙

在你的應用程序進行藍牙通信前,應該確定你的設備支持藍牙,並且是打開狀態的。

如果該設備不支持藍牙,你應該在應用中避開藍牙的使用。如果設備支持藍牙但是處於關閉狀態,你應該在當前應用中請求用戶啓用藍牙。該過程可以用BluetoothAdapter分兩步完成。

1.獲取BluetoothAdapter

BluetoothAdapter對象是所有藍牙Activity所必須的,開發者可以通過靜態方法getDefaultAdapter()取得BluetoothAdapter對象。該方法返回一個代表本地藍牙適配器的BluetoothAdapter對象。整個系統中只有一個藍牙適配器,應用程序可以通過該對象來與它交互。如果getDefaultAdapter()方法返回爲null,則表示該設備不支持藍牙。

[java] view plaincopy
  1. BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  
  2. if (mBluetoothAdapter == null) {  
  3.     // Device does not support Bluetooth  
  4. }  

2.打開藍牙

接下來,你應該確保藍牙是打開的。通過isEnabled()方法可以檢查當前藍牙是否處於打開狀態,如果方法返回false,則藍牙處於關閉狀態。開發者可以通過使用帶有ACTION_REQUEST_ENABLE這個action的Intent調用startActivityForResult()來請求用戶打開藍牙。這將會請求通過系統設置來打開藍牙,並不會關閉當前應用。代碼如下:

[java] view plaincopy
  1. if (!mBluetoothAdapter.isEnabled()) {  
  2.     Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
  3.     startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);  
  4. }  
接下來會彈出一個對話框請求用戶打開藍牙,如下圖所示:

如果用戶選擇"Yes",系統將會開始打開藍牙並且在結束操作時返回你的應用程序。

傳遞給startActivityForResult()的常量REQUEST_ENABLE_BT是一個本地定義的整數(必須大於0)。在常量會作爲回調方法onActivityResult()中的requestCode參數返回。

如果打開藍牙成功,Activity在onActivityResult()方法中會收到返回碼RESULT_OK,如果應用由於某原因打開失敗或者用戶選擇了"No",則Activity會收到返回碼RESULT_CANCELED

另外,應用程序可以監聽ACTION_STATE_CHANGED廣播,系統會在藍牙狀態發生改變時發出該廣播。該廣播包含EXTRA_STATEEXTRA_PREVIOUS_STATE兩個屬性,分別代表當前狀態和之前狀態。屬性的值可能爲STATE_TURNING_ONSTATE_ONSTATE_TURNING_OFF以及STATE_OFF。當應用運行時監聽該廣播對檢測藍牙狀態很有幫助。

注意:啓用藍牙的可發現(discoverability)將會自動打開藍牙。如果你想在進行藍牙活動前持續地打開藍牙的可發現性,你可以跳過上面的步驟2。具體可以參考enabling discoverability

找到設備

使用BluetoothAdapter,你可以通過藍牙發現服務或者已配對設備列表發現附近的藍牙設備。

設備發現是一個搜索附近藍牙設備並向每個設備請求基本信息的一個掃描過程。然而,附近的藍牙設備只有在打開了可發現性後纔會對發現請求作出迴應。如果一臺設備是可發現的,它會對發現請求迴應一些基本信息,如設備名稱、類別以及MAC地址。通過這些信息,執行發現服務的設備可以選擇性的與已發現的設備進行連接。

當第一次與遠程設備連接時,系統將會呈現給用戶一個配對請求。當一臺設備配對後,該設備的基本信息(設備名稱、類別和MAC地址)都被保存下來並且可以通過藍牙的API讀取出來。知道了一臺遠程設備的MAC地址後,可以隨時發起連接而不需要經過發現(假設設備在可連接範圍內)。

記住已配對和已連接是有區別的。已配對錶明兩臺設備知道對方的存在,有一個共同的可用於認證的link-key,並且能夠與對方建立一個加密連接。已連接表明兩臺設備共享一個RFCOMM信道(channel)並且能夠向對方發送數據。當前的android 藍牙API在建立RFCOMM連接前需要進行配對操作。(當你用andorid 藍牙API建立加密連接時配對操作會自動完成)。

接下來的部分描述如何找到已配對的設備以及通過設備發現服務發現其它設備。

注意:android設備默認都是不可發現的。用戶可以通過系統設置讓設備在一定時間內可以被發現,應用程序也可以在不離開當前應用的情況下請求用戶打開設備的可發現性。在之後的內容中會講到如何enable discoverability

查詢已配對設備

在進行藍牙發現之前,有必要查詢已配對設備列表檢查所期望設備是否是已知的。通過使用getBondedDevices()方法即可,它會返回一系列代表已配對設備的BluetoothDevice。例如,你可以查詢所有的已配對設備然後用一個ArrayAdapter給用戶顯示每一個設備的名稱。

[java] view plaincopy
  1. Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();  
  2. // If there are paired devices  
  3. if (pairedDevices.size() > 0) {  
  4.     // Loop through paired devices  
  5.     for (BluetoothDevice device : pairedDevices) {  
  6.         // Add the name and address to an array adapter to show in a ListView  
  7.         mArrayAdapter.add(device.getName() + "\n" + device.getAddress());  
  8.     }  
  9. }  

如果需要建立連接,所有所需要的只有BluetoothDevice對象中的MAC地址。在上面的例子中,它作爲ArrayAdapter的一部分保存下來並展示給用戶,之後MAC地址可以被提取出來用來建立連接。關於建立連接部分可以參見Connecting Devices

發現設備

要開始發現設備,只需要調用startDiscovery()方法。該過程是異步的,方法會立即返回一個boolean值以表明發現服務是否成功開啓。發現過程通常包括大約12秒的查詢掃描,接下來是掃描每一個設備獲取其名稱。

應用程序必須爲ACTION_FOUND這個Intent註冊一個BroadcastReceiver用來接收每一臺發現的設備的信息。每發現一臺設備,系統都會廣播ACTION_FOUND這個Intent。這個intent捆綁了含TRA_DEVICEEXTRA_CLASS兩個屬性,分別包含一個BluetoothDevice和一個BluetoothClass對象。下面的例子展示了開發者應該如何處理發現到設備的廣播:

[java] view plaincopy
  1. // Create a BroadcastReceiver for ACTION_FOUND  
  2. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {  
  3.     public void onReceive(Context context, Intent intent) {  
  4.         String action = intent.getAction();  
  5.         // When discovery finds a device  
  6.         if (BluetoothDevice.ACTION_FOUND.equals(action)) {  
  7.             // Get the BluetoothDevice object from the Intent  
  8.             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
  9.             // Add the name and address to an array adapter to show in a ListView  
  10.             mArrayAdapter.add(device.getName() + "\n" + device.getAddress());  
  11.         }  
  12.     }  
  13. };  
  14. // Register the BroadcastReceiver  
  15. IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);  
  16. registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy  

注意:執行設備發現對藍牙適配器是很耗費資源的,一旦你發現需要連接的設備,確保在建立連接前調用cancelDiscovery()來停止發現服務。另外,如果你已經與一臺設備建立了連接,執行設備發現服務將會明顯地壓縮該連接的可用帶寬,所以在連接狀態時你不應該執行設備發現服務。

啓用可發現性

如果你希望你的設備可以被其它設備發現,調用startActivityForResult(Intent, int),在參數中加入ACTION_REQUEST_DISCOVERABLE這個Intent。這將會請求用戶在系統設置中打開可發現性模式。設備默認會在120s內可以被其它設備發現,你也可以在intent中加入EXTRA_DISCOVERABLE_DURATION這個屬性來定義可被發現的時間。應用可以定義的最大時間是3600s,0代表設備始終不可被發現,小於0或者大於3600的值都會默認設置爲120s。例如如下代碼設置300s內可以被發現:

[java] view plaincopy
  1. Intent discoverableIntent = new  
  2. Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);  
  3. discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);  
  4. startActivity(discoverableIntent);  

系統會彈出一個如下所示的對話框請求用戶設置設備可被發現:

如果用戶點擊”Yes",設備在指定時間內將會可被發現,Activity將會調用onActivityResult())回調方法,返回碼等於設置的可被發現時間。如果用戶點擊了"No"或者有異常,返回碼將是RESULT_CANCELED

注意:如果設備藍牙還沒有打開,啓用藍牙的可發現性將會自動打開藍牙。

設備將會在該時間內保持可被發現。如果你想在設備的可發現性改變時得到通知,你可以爲ACTION_SCAN_MODE_CHANGED Intent註冊一個BroadcastReceiver。這個intent包含EXTRA_SCAN_MODEEXTRA_PREVIOUS_SCAN_MODE屬性,分別表示當前的和之前的掃描模式。可能的值爲SCAN_MODE_CONNECTABLE_DISCOVERABLESCAN_MODE_CONNECTABLESCAN_MODE_NONE,分別代表設備處於可被發現模式、不處於可發現模式但是可以接收連接、不處於可發現模式並且不可以接收連接。

如果你想連接到一臺遠程設備你不必打開設備的可發現性。只有在你的應用程序在運行服務器Socket並且希望接收連接時才需要打開可被發現性,因爲遠程設備在建立連接前需要發現希望連接的設備。

連接設備

爲了讓你的應用在兩臺設備間建立連接,你必須同時實現server端和client端機制,因爲其中一臺設備必須打開server socket然後另外一臺設備建立連接(通過server端設備的MAC地址來建立連接)。server端和client在同一RFCOMM信道上有連接好的BluetoothSocket則認爲它們互相連接到了對方。此時,每臺設備都可以獲取IO流並且可以開始數據傳輸,在後面的Managing a Connection中會詳細講解。這部分描述如何在兩臺設備間建立連接。

server端和client端通過不同的方法獲取BluetoothSocket對象,server端會在接受一個連接請求時取得它,client端會在向server端打開一個RFCOMM信道時取得它。

一種實現方法是每臺設備都準備作爲server端,每個設備都有打開的server socket在監聽連接請求,然後其中一臺設備可以向另一臺設備發起連接並且成爲client端。或者一臺設備打開server socket明確地作爲server端,另外一臺設備只需要發起請求就可以。

注意:如果兩臺設備之前沒有配對,系統會自動顯示請求配對的通知或者顯示如下對話框給用戶:


所以在連接其它設備時,應用程序無需關心兩臺設備是否已經配對。你的連接嘗試會被祖塞直到用戶成功配對,或者在用戶拒絕配對、配對失敗、配對超時時失敗。

作爲server端

如果你想連接兩臺設備,其中一臺設備必須持有BluetoothServerSocket對象以作爲server,server socket的作用是監聽連接請求並且在接收請求時提供一個連接好的BluetoothSocket對象。當從BluetoothServerSocket中取得BluetoothSocket對象後,BluetoothServerSocket對象就沒用了,除非你需要接收更多的請求。

下面是建立一個server socket並接收一個請求的基本步驟

1.通過調用listenUsingRfcommWithServiceRecord(String, UUID)方法取得一個BluetoothServerSocket對象

關於UUID:通用唯一標識符(Universally Unique Identifier)是一種用來唯一標識信息的128位標準化格式的字符串ID。重點是UUID數字足夠大,你可以任意選取隨機數而不用擔心重複。在這裏,它用來唯一標識你的應用裏的藍牙服務。要在應用中使用UUID,你可以使用網上的UUID生成器,然後通過fromString(String)來生成一個UUID對象。

上面的string是你的服務的標識名,系統將會自動將它寫入一個新的SDP(Service Discovery Protocol)數據庫條目(名字是任意的,可以簡單的寫爲應用程序名)。UUID也包含在SDP條目中並且將會是與客戶端達成連接的基礎。也就是,當客戶端試圖連接這一臺設備時,它會帶有一個UUID用來唯一標識它想連接的服務,要想連接被接受UUID必須匹配。

2.調用accept()開始監聽連接請求

這是一個阻塞方法,只有在接收一個連接或者有異常發生時它纔會返回。當遠程設備發送的請求中的UUID與server端設備正在監聽的server socket中註冊的UUID一致時連接請求才會被接受。如果成功了,accept()方法會返回一個連接上的BluetoothSocket

3.除非你想接收其它連接,否則調用close()

該方法釋放server socket的資源,但是不會關閉之前由accept()方法返回的連接上的BluetoothSocket。不同於TCP/IP,RFCOMM在同一時間同一信道只能允許一臺client端連接。所以大多數情況下在BluetoothServerSocket接受一個請求後立即調用close()很重要。

accept()方法不應該在主要的activity UI線程中執行,因爲它是一個阻塞方法並且會阻止應用的UI交互。通常情況下與BluetoothServerSocketBluetoothSocket的操作都放在新線程中進行。如果要退出一個阻塞的方法如accept(),則應該在另外一個線程中調用BluetoothServerSocket(或者BluetoothSocket)的close()方法,這樣阻塞方法會立即返回。注意BluetoothServerSocketBluetoothSocket中的方法都是線程安全的。

實例

如下是一個簡單的server端接收連接請求的線程:

[java] view plaincopy
  1. private class AcceptThread extends Thread {  
  2.     private final BluetoothServerSocket mmServerSocket;  
  3.    
  4.     public AcceptThread() {  
  5.         // Use a temporary object that is later assigned to mmServerSocket,  
  6.         // because mmServerSocket is final  
  7.         BluetoothServerSocket tmp = null;  
  8.         try {  
  9.             // MY_UUID is the app's UUID string, also used by the client code  
  10.             tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);  
  11.         } catch (IOException e) { }  
  12.         mmServerSocket = tmp;  
  13.     }  
  14.    
  15.     public void run() {  
  16.         BluetoothSocket socket = null;  
  17.         // Keep listening until exception occurs or a socket is returned  
  18.         while (true) {  
  19.             try {  
  20.                 socket = mmServerSocket.accept();  
  21.             } catch (IOException e) {  
  22.                 break;  
  23.             }  
  24.             // If a connection was accepted  
  25.             if (socket != null) {  
  26.                 // Do work to manage the connection (in a separate thread)  
  27.                 manageConnectedSocket(socket);  
  28.                 mmServerSocket.close();  
  29.                 break;  
  30.             }  
  31.         }  
  32.     }  
  33.    
  34.     /** Will cancel the listening socket, and cause the thread to finish */  
  35.     public void cancel() {  
  36.         try {  
  37.             mmServerSocket.close();  
  38.         } catch (IOException e) { }  
  39.     }  
  40. }  

在上面的例子中,只期望連接一個client端,只要接受了一個連接,取得了BluetoothSocket對象,應用程序就會將取得的BluetoothSocket對象發送到另外一個線程中,關閉BluetoothServerSocket並且跳出循環。

注意當accept()方法返回BluetoothSocket時,該socket已經連接,你不應該再去調用connect()(在client端完成)。

代碼中manageConnectedSocket()是一個虛構的方法,它會建立一個線程並用來傳輸數據,在下面的Managing a Connection中會講到。

只要你完成接受連接請求就應該關閉BluetoothServerSocket。比如,取得BluetoothSocket對象後就應該調用close()方法。你也許需要在線程中提供一個公有方法來關閉私有的BluetoothSocket。You may also want to provide a public method in your thread that can close the privateBluetoothSocket in the event that you need to stop listening on the server socket.

作爲client端

爲了與遠程設備(開有server socket的設備)連接,你首先必須獲取一個代表遠程設備的BluetoothDevice對象。如何獲取BluetoothDevice對象在前面的Finding Devices部分已經講到了。接下來你應該用BluetoothDevice對象獲取一個BluetoothSocket對象然後建立連接。

下面是基本的步驟:

1.使用BluetoothDevice對象,調用createRfcommSocketToServiceRecord(UUID)方法取得一個BluetoothSocket對象,這樣就實現了要連接到BluetoothDevice的一個BluetoothSocket對象。這裏傳入的UUID必須與server端用listenUsingRfcommWithServiceRecord(String, UUID)方法建立BluetoothServerSocket時使用的UUID一致。保證使用相同的UUID只需要在你的應用中將它寫固定,然後在server端和client端的代碼中都引用它即可。

2.調用connect()來建立連接

調用該方法後,系統會對遠程設備執行SDP查找以匹配UUID。如果查找成功並且遠程設備接受連接,它將會共享連接過程中的RFCOMM信道並且connect()方法會返回。該方法是一個阻塞方法,如果因爲某些原因連接失敗或者connect()方法超時(大約12秒鐘),那麼它會拋出一個異常。因爲connect()方法是一個阻塞方法,所以連接過程在一個與主線程區分的線程中進行。

注意:你應該確保在執行connect()時你的設備沒有在進行掃描,如果正在掃描,那麼連接過程會非常慢並且很容易失敗。

實例

下面是一個建立藍牙連接的線程實例:

[java] view plaincopy
  1. private class ConnectThread extends Thread {  
  2.     private final BluetoothSocket mmSocket;  
  3.     private final BluetoothDevice mmDevice;  
  4.    
  5.     public ConnectThread(BluetoothDevice device) {  
  6.         // Use a temporary object that is later assigned to mmSocket,  
  7.         // because mmSocket is final  
  8.         BluetoothSocket tmp = null;  
  9.         mmDevice = device;  
  10.    
  11.         // Get a BluetoothSocket to connect with the given BluetoothDevice  
  12.         try {  
  13.             // MY_UUID is the app's UUID string, also used by the server code  
  14.             tmp = device.createRfcommSocketToServiceRecord(MY_UUID);  
  15.         } catch (IOException e) { }  
  16.         mmSocket = tmp;  
  17.     }  
  18.    
  19.     public void run() {  
  20.         // Cancel discovery because it will slow down the connection  
  21.         mBluetoothAdapter.cancelDiscovery();  
  22.    
  23.         try {  
  24.             // Connect the device through the socket. This will block  
  25.             // until it succeeds or throws an exception  
  26.             mmSocket.connect();  
  27.         } catch (IOException connectException) {  
  28.             // Unable to connect; close the socket and get out  
  29.             try {  
  30.                 mmSocket.close();  
  31.             } catch (IOException closeException) { }  
  32.             return;  
  33.         }  
  34.    
  35.         // Do work to manage the connection (in a separate thread)  
  36.         manageConnectedSocket(mmSocket);  
  37.     }  
  38.    
  39.     /** Will cancel an in-progress connection, and close the socket */  
  40.     public void cancel() {  
  41.         try {  
  42.             mmSocket.close();  
  43.         } catch (IOException e) { }  
  44.     }  
  45. }  

注意在建立連接前調用了cancelDiscovery()方法,在建立連接前你應該總是這麼做而且不用考慮它是否有在掃描(如果想檢查可以調用isDiscovering()))。

代碼中manageConnectedSocket()是一個虛構的方法,它會建立一個線程並用來傳輸數據,在下面的Managing a Connection中會講到。

當你操作完BluetoothSocket時應該調用close()來清理資源,它將會立即關閉所連接的socket並清理內部資源。

管理連接

當你成功連接兩臺或者多臺設備後,每一臺設備都會擁有一個連接好的BluetoothSocket對象,通過它你可以在設備間共享數據。傳輸數據的通用步驟如下:

1.通過getInputStream()getOutputStream()分別取得InputStreamOutputStream對象。

2.用read(byte[])write(byte[])從/向數據流讀取/寫入數據。

有些實現上的細節需要考慮,最主要是爲所有的流讀寫單獨開啓線程。read(byte[])方法會一直阻塞直到流中有內容可以讀取,write(byte[])方法並不總是阻塞的,但是在遠程設備調用read(byte[])方法比較慢並且緩衝區滿了的情況下也會造成阻塞。所以,線程中的主循環應該專門用來讀取InputStream中的數據,並且在線程中寫一個公開的方法用來向OutputStream中寫入數據。

下面是代碼的寫法:

[java] view plaincopy
  1.    
  2. private class ConnectedThread extends Thread {  
  3.     private final BluetoothSocket mmSocket;  
  4.     private final InputStream mmInStream;  
  5.     private final OutputStream mmOutStream;  
  6.    
  7.     public ConnectedThread(BluetoothSocket socket) {  
  8.         mmSocket = socket;  
  9.         InputStream tmpIn = null;  
  10.         OutputStream tmpOut = null;  
  11.    
  12.         // Get the input and output streams, using temp objects because  
  13.         // member streams are final  
  14.         try {  
  15.             tmpIn = socket.getInputStream();  
  16.             tmpOut = socket.getOutputStream();  
  17.         } catch (IOException e) { }  
  18.    
  19.         mmInStream = tmpIn;  
  20.         mmOutStream = tmpOut;  
  21.     }  
  22.    
  23.     public void run() {  
  24.         byte[] buffer = new byte[1024];  // buffer store for the stream  
  25.         int bytes; // bytes returned from read()  
  26.    
  27.         // Keep listening to the InputStream until an exception occurs  
  28.         while (true) {  
  29.             try {  
  30.                 // Read from the InputStream  
  31.                 bytes = mmInStream.read(buffer);  
  32.                 // Send the obtained bytes to the UI activity  
  33.                 mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)  
  34.                         .sendToTarget();  
  35.             } catch (IOException e) {  
  36.                 break;  
  37.             }  
  38.         }  
  39.     }  
  40.    
  41.     /* Call this from the main activity to send data to the remote device */  
  42.     public void write(byte[] bytes) {  
  43.         try {  
  44.             mmOutStream.write(bytes);  
  45.         } catch (IOException e) { }  
  46.     }  
  47.    
  48.     /* Call this from the main activity to shutdown the connection */  
  49.     public void cancel() {  
  50.         try {  
  51.             mmSocket.close();  
  52.         } catch (IOException e) { }  
  53.     }  
  54. }  

線程的構造方法會獲取IO流,當線程執行時,它會等待從輸入流中來的數據。當read(byte[])讀取到字節返回時,數據會由一個Handler發送給Activity處理,然後會進入下一次循環,繼續等待輸入流的內容。

向其它設備發送數據只需要在Activity中調用線程的write()方法,在方法中傳入字節。該方法會調用write(byte[])將數據發送到遠程設備。

當不再使用藍牙連接時,應該調用cancel()方法來關閉luetoothSocket

關於藍牙API的使用實例,可以參考Bluetooth Chat sample app

原文地址:http://developer.android.com/guide/topics/connectivity/bluetooth.html


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