Android平臺包含了對藍牙網絡協議棧的支持,它容許一個藍牙設備跟其餘的藍牙設備進行無線的數據交換。應用程序經過Android藍牙API提供訪問藍牙的功能。這些API會把應用程序無線鏈接到其餘的藍牙設備上,具備點到點和多點無線特徵。
使用藍牙API,Android應用程序可以執行如下功能:
1. 掃描其餘藍牙設備
2. 查詢本地已經配對的藍牙適配器
3. 創建RFCOMM通道
4. 經過服務發現來鏈接其餘設備
5. 在設備間傳輸數據
6. 管理多個藍牙鏈接
基礎
本文介紹如何使用Android的藍牙API來完成使用藍牙通訊所須要的四項主要任務:設置藍牙、查找已配對或區域內可用的藍牙設備、鏈接設備、設備間傳輸數據。
全部的可用的API都在android.bluetooth包中。如下概要的介紹建立藍牙鏈接所須要的類和接口:
BluetoothAdapter
表明本地藍牙適配器(藍牙無線)。BluetoothAdapter是全部藍牙交互的入口。使用這個類,你可以發現其餘的藍牙設備,查詢已配對設備的列表,使用已知的MAC地址來實例化一個BluetoothDevice對象,而且建立一個BluetoothServerSocket對象來監聽與其餘設備的通訊。
BluetoothDevice
表明一個遠程的藍牙設備。使用這個類經過BluetoothSocket或查詢諸如名稱、地址、類和配對狀態等設備信息來請求跟遠程設備的鏈接。
BluetoothSocket
表明藍牙socket的接口(相似TCP的Socket)。這是容許一個應用程序跟另外一個藍牙設備經過輸入流和輸出流進行數據交換的鏈接點。
BluetoothServerSocket
表明一個打開的監聽傳入請求的服務接口(相似於TCP的ServerSocket)。爲了鏈接兩個Android設備,一個設備必須用這個類打開一個服務接口。當遠程藍牙設備請求跟本設備創建鏈接請求時,BluetoothServerSocket會在鏈接被接收時返回一個被鏈接的BluetoothSocket對象。
BluetoothClass
描述了藍牙設備的通常性特徵和功能。這個類是一個只讀的屬性集,這些屬性定義了設備的主要和次要設備類和服務。可是,這個類並不保證描述了設備所支持的全部的藍牙配置和服務,可是這種對設備類型的提示是有益的。
BluetoothProfile
表明一個藍牙配置的接口。藍牙配置是基於藍牙通訊的設備間的無線接口規範。一個例子是免提的配置。更多的配置討論,請看下文的用配置來工做。
BluetoothHeadset
提供對使用藍牙耳機的移動電話的支持。它同時包含了Bluetooth Headset和Hands-Free(v1.5)的配置。
BluetoothA2dp
定義如何把高品質的音頻經過藍牙鏈接從一個設備流向另外一個設備。「A2DP」是Advanced Audio Distribution Profile的縮寫。
BluetoothHealth
表明一個健康保健設備配置的控制藍牙服務的代理。
BluetoothHealthCallback
用於實現BluetoothHealth回調的抽象類。你必須繼承這個類,並實現它的回調方法,來接收應用程序的註冊狀態和藍牙通道狀態變化的更新。
BluetoothHealthAppConfiguration
表明藍牙相關的第三方健康保健應用程序所註冊的與遠程藍牙健康保健設備進行通訊的配置。
BluetoothProfile.ServiceListener
BluetoothProfile IPC客戶端鏈接或斷開服務的通知接口(它是運行特俗配置的內部服務)。
Android的聯通性---Bluetooth(二)
藍牙權限
爲了在你的應用程序中使用藍牙功能,至少要聲明兩個藍牙權限(BLUETOOTH和BLUETOOTH_ADMIN)中的一個。
爲了執行任何藍牙通訊(如請求鏈接、接收鏈接和傳輸數據),你必須申請BLUETOOTH權限。
爲了啓動設備發現或維護藍牙設置,你必須申請BLUETOOTH_ADMIN權限。大多數須要這個權限的應用程序,僅僅是爲可以發現本地的藍牙設備。這個權限所授予的其餘能力應該不被使用,除非是電源管理的應用程序,它會在依據用戶的請求來修改藍牙設置。注意:若是你使用了BLUETOOTH_ADMIN權限,那麼必需要有BLUETOOTH權限。
在你的應用程序清單文件中聲明藍牙權限,例如:
...
< /manifest>
關於聲明應用程序權限的更多信息,請參閱
設置藍牙
在應用程序可以利用藍牙通道通訊以前,須要確認設備是否支持藍牙通訊,若是支持,要確保它是可用的。
若是不支持藍牙,那麼你應該有好的禁用全部藍牙功能。若是支持藍牙,可是被禁用的,那麼你要在不離開你的應用程序的狀況下,請求用戶啓用藍牙功能,這種設置要使用BluetoothAdapter對象,在如下兩個步驟中完成。
1. 得到BluetoothAdapter對象
BluetoothAdapter對象是全部藍牙活動都須要的,要得到這個對象,就要調用靜態的getDefaultAdapter()方法。這個方法會返回一個表明設備本身的藍牙適配器的BluetoothAdapter對象。整個系統有一個藍牙適配器,你的應用程序可以使用這個對象來進行交互。若是getDefaultAdapter()方法返回null,那麼該設備不支持藍牙,你的處理也要在此結束。例如:
BluetoothAdapter mBluetoothAdapter =BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter ==null){
// Device does not support Bluetooth
}
2. 啓用藍牙功能
接下來,你須要確保藍牙是可用的。調用isEnabled()方法來檢查當前藍牙是否可用。若是這個方法返回false,那麼藍牙是被禁用的。要申請啓用藍牙功能,就要調用帶有ACTION_REQUEST_ENABLE操做意圖的startActivityForResult()方法。它會給系統設置發一個啓用藍牙功能的請求(不終止你的應用程序)。例如:
if(!mBluetoothAdapter.isEnabled()){
Intent enableBtIntent =newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
這時會顯示一個請求用戶啓用藍牙功能的對話框,如圖1所示:
圖1.啓用藍牙功能的對話框。
若是用戶響應「Yes」,那麼系統會開始啓用藍牙功能,完成啓動過程(有可能失敗),焦點會返回給你的應用程序。
傳遞給startActivityForResult()方法的REQUEST_ENABLE_BT常量,是一個你的應用程序中定義的整數(它必須大於0),系統會把它做爲requestCode參數返回到你的onActivityResult()回調實現中。
若是藍牙功能啓用成功,你的Activity會在onActivityResult()回調中接收到RESULT_OK結果,若是藍牙沒有被啓動(或者用戶響應了「No」),那麼該結果編碼是RESULT_CANCELED。
可選地,你的應用程序還能夠監聽ACTION_STATE_CHANGED廣播Intent,不管藍牙狀態什麼時候改變,系統都會廣播這個Intent。這個廣播包含的附加字段EXTRA_STATE和EXTRA_PREVIOUS_STATE中分別指明瞭新的和舊的藍牙狀態。這些附加字段中可能的值是:STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF和STATE_OFF。監聽這個廣播對於在應用程序運行時檢測藍牙的狀態是有用的。
提示:啓用可發現能力會自動啓動藍牙功能。若是你計劃在執行藍牙活動以前,要始終啓用設備的可發現機制,就能夠跳過上面的第2步,詳細請參閱下文「啓用藍牙可發現」。
Android的聯通性---Bluetooth(三)
查找設備
使用BluetoothAdapter對象,可以經過設備發現或查詢已配對的設備列表來找到遠程的藍牙設備。
設備發現是一個掃描過程,該過程搜索本地區域內可用的藍牙設備,而後請求一些彼此相關的一些信息(這個過程被叫作「發現」、「查詢」或「掃描」)。可是,本地區域內的藍牙設備只有在它們也啓用了可發現功能時,纔會響應發現請求。若是一個設備是可發現的,那麼它會經過共享某些信息(如設備名稱、類別和惟一的MAC地址)來響應發現請求。使用這些信息,執行發現處理的設備可以有選擇的初始化跟被發現設備的鏈接。
一旦跟遠程的設備創建的首次鏈接,配對請求就會自動的被展示給用戶。當設備完成配對,相關設備的基本信息(如設備名稱、類別和MAC地址)就會被保存,並可以使用藍牙API來讀取。使用已知的遠程設備的MAC地址,在任什麼時候候都可以初始化一個鏈接,而不須要執行發現處理(假設設備在可鏈接的範圍內)。
要記住配對和鏈接之間的差別。配對意味着兩個設備對彼此存在性的感知,它們之間有一個共享的用於驗證的鏈接密鑰,用這個密鑰兩個設備之間創建被加密的鏈接。鏈接意味着當前設備間共享一個RFCOMM通道,而且可以被用於設備間的數據傳輸。當前Android藍牙API在RFCOMM鏈接被創建以前,要求設備之間配對。(在使用藍牙API初始化加密鏈接時,配對是自動被執行的。)
如下章節介紹如何發現已配對的設備,或發現新的使用了可發現功能的設備。
注意:默認Android設備是不可發現的。用戶可以經過系統設置在限定的時間內變成可發現的設備,或者應用程序可以請求用戶啓用可發現性,而不離開應用程序。如何啓用可發現性,會在下文來討論。
查詢配對設備
在執行設備發現以前,應該先查詢已配對的設備集合,來看指望的設備是不是已知的。調用getBondedDevices()方法來完成這件工做。這個方法會返回一個表明已配對設備的BluetoothDevice對象的集合。例如,你可以查詢全部的配對設備,而後使用一個ArrayAdapter對象把每一個已配對設備的名稱顯示給用戶。
Set 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地址。在這個例子中,MAC地址被做爲ArrayAdapter的一部分來保存,並顯示給用戶。隨後,該MAC地址可以被提取用於初始化鏈接。
發現設備
簡單的調用startDiscovery()方法就能夠開始發現設備。該過程是異步的,而且該方法會當即返回一個布爾值來指明發現處理是否被成功的啓動。一般發現過程會查詢掃描大約12秒,接下來獲取掃描發現的每一個設備的藍牙名稱。
爲了接收每一個被發現設備的的信息,你的應用程序必須註冊一個ACTION_FOUND類型的廣播接收器。對應每一個藍牙設備,系統都會廣播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
警告:執行設備發現,對於藍牙適配器來講是一個沉重的過程,它會消耗大量的資源。一旦發現要鏈接設備,在嘗試鏈接以前必定要確認用cancelDiscovery()方法來終止發現操做。另外,若是已經有一個跟設備的鏈接,那麼執行發現會明顯的減小鏈接的可用帶寬,所以在有鏈接的時候不該該執行發現處理。
Android的聯通性---Bluetooth(四)
啓用設備的可發現性
若是要讓本地設備能夠被其餘設備發現,那麼就要調用ACTION_REQUEST_DISCOVERABLE操做意圖的startActivityForResult(Intent, int)方法。這個方法會向系統設置發出一個啓用可發現模式的請求(不終止應用程序)。默認狀況下,設備的可發現模式會持續120秒。經過給Intent對象添加EXTRA_DISCOVERABLE_DURATION附加字段,能夠定義不一樣持續時間。應用程序可以設置的最大持續時間是3600秒,0意味着設備始終是可發現的。任何小於0或大於3600秒的值都會自動的被設爲120秒。例如,如下代碼把持續時間設置爲300秒:
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
如圖2所示,申請用戶啓用設備的可發現模式時,會顯示這樣一個對話框。若是響應「Yes」,那麼設備的可發現模式會持續指定的時間,並且你的Activity會接收帶有結果代碼等於可發現設備持續時間的onActivityResult()回調方法的調用。若是用戶響應「No」或有錯誤發生,則結果代碼等於RESULT_CANCELED.
圖2.啓用可發現性對話框。
注意:若是設備沒有開啓藍牙功能,那麼開啓設備的可發現模式會自動開啓藍牙。
在可發現模式下,設備會靜靜的把這種模式保持到指定的時長。若是你想要在可發現模式被改變時得到通知,那麼你能夠註冊一個ACTION_SCAN_MODE_CHANGED類型的Intent廣播。這個Intent對象中包含了EXTRA_SCAN_MODE和EXTRA_PREVIOUS_SCAN_MODE附加字段,它們會分別告訴你新舊掃描模式。它們每一個可能的值是:SCAN_MODE_CONNECTABLE_DISCOVERABLE,SCAN_MODE_CONNECTABLE或SCAN_MODE_NONE,它們分別指明設備是在可發現模式下,仍是在可發現模式下但依然可接收鏈接,或者是在可發現模式下並不能接收鏈接。
若是你要初始化跟遠程設備的鏈接,你不須要啓用設備的可現性。只有在你想要把你的應用程序做爲服務端來接收輸入鏈接時,才須要啓用可發現性,由於遠程設備在跟你的設備鏈接以前必須可以發現它。
鏈接設備
爲了讓兩個設備上的兩個應用程序之間創建鏈接,你必須同時實現服務端和客戶端機制,由於一個設備必須打開服務端口,同時另外一個設備必須初始化跟服務端設備的鏈接(使用服務端的MAC地址來初始化一個鏈接)。當服務端和客戶端在相同的RFCOMM通道上有一個BluetoothSocket鏈接時,纔可以被認爲是服務端和客戶端之間創建了鏈接。這時,每一個設備可以得到輸入和輸出流,而且可以彼此開始傳輸數據。
服務端設備和客戶端設備彼此獲取所需的BluetoothSocket的方法是不一樣的。服務端會在接收輸入鏈接的時候接收到一個BluetoothSocket對象。客戶端會在打開跟服務端的RFCOMM通道時接收到一個BluetoothSocket對象。
一種實現技術是自動的準備一個設備做爲服務端,以便在每一個設備都會有一個服務套接字被打開,並監聽鏈接請求。當另外一個設備初始化一個跟服務端套接字的鏈接時,它就會變成一個客戶端。另外一種方法,一個設備是明確的」host」鏈接,而且根據要求打開一個服務套接字,而其餘的設備只是簡單的初始化鏈接。
注意:若是兩個設備以前沒有配對,那麼Android框架會在鏈接過程期間,自動的顯示一個配對請求通知或對話框給用戶,如圖3所示。所以在試圖鏈接設備時,你的應用程序不須要關心設備是否被配對。FRCOMM的嘗試性鏈接會一直阻塞,一直到用戶成功的配對,或者是因用戶拒絕配對或配對超時而失敗。
圖3.藍牙配對對話框
Android的聯通性---Bluetooth(五)
做爲鏈接的服務端
當你想要鏈接兩個設備時,一個必須經過持有一個打開的BluetoothServerSocket對象來做爲服務端。服務套接字的用途是監聽輸入的鏈接請求,而且在一個鏈接請求被接收時,提供一個BluetoothSocket鏈接對象。在從BluetoothServerSocket對象中獲取BluetoothSocket時,BluetoothServerSocket可以(而且也應該)被廢棄,除非你想要接收更多的鏈接。
如下是創建服務套接字和接收一個鏈接的基本過程。
1. 調用listenUsingRfcommWithServiceRecord(String, UUID)方法來得到一個BluetoothServerSocket對象。該方法中的String參數是一個可識別的你的服務端的名稱,系統會自動的把它寫入設備上的Service Discovery Protocol(SDP)數據庫實體(該名稱是任意的,而且能夠簡單的使用你的應用程序的名稱)。UUID參數也會被包含在SDP實體中,而且是跟客戶端設備鏈接的基本協議。也就是說,當客戶端嘗試跟服務端鏈接時,它會攜帶一個它想要鏈接的服務端可以惟一識別的UUID。只有在這些UUID徹底匹配的狀況下,鏈接纔可能被接收。
2. 經過調用accept()方法,啓動鏈接請求。這是一個阻塞調用。只有在鏈接被接收或發生異常的狀況下,該方法才返回。只有在發送鏈接請求的遠程設備所攜帶的UUID跟監聽服務套接字所註冊的一個UUID匹配的時候,該鏈接才被接收。鏈接成功,accept()方法會返回一個被鏈接的BluetoothSocket對象。
3. 除非你想要接收其餘鏈接,不然要調用close()方法。該方法會釋放服務套接字以及它所佔用的全部資源,但不會關閉被鏈接的已經有accept()方法所返回的BluetoothSocket對象。跟TCP/IP不同,每一個RFCOMM通道一次只容許鏈接一個客戶端,所以在大多數狀況下,在接收到一個鏈接套接字以後,當即調用BluetoothServerSocket對象的close()方法是有道理的。
accept()方法的調用不該該在主Activity的UI線程中被執行,由於該調用是阻塞的,這會阻止應用程序的其餘交互。一般在由應用程序所管理的一個新的線程中來使用BluetoothServerSocket對象或BluetoothSocket對象來工做。要終止諸如accept()這樣的阻塞調用方法,就要從另外一個線程中調用BluetoothServerSocket對象(或BluetoothSocket對象)的close()方法,這時阻塞會當即返回。注意在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對象時,套接字已是被鏈接的,所以你不該該再調用像客戶端那樣調用connect()方法了。
應用程序中的manageConnectedSocket()方法是一個自定義方法,它會初始化用於傳輸數據的線程。
一般,一旦你監聽完成呼入鏈接,就應該關閉BluetoothServerSocket對象。在這個示例中,close()方法是在得到BluetoothSocket對象以後被調用的。你可能還想要提供一個公共方法,以便在你的線程中可以關閉你想要終止監聽的服務套接字事件中的私有BluetoothSocket對象。
做爲鏈接的客戶端
爲了初始化一個與遠程設備(持有打開的服務套接字的設備)的鏈接,首先必須獲取個表明遠程設備的BluetoothDevice對象。而後使用BluetoothDevice對象來獲取一個BluetoothSocket對象,並初始化該鏈接。
如下是一個基本的鏈接過程:
1. 經過調用BluetoothDevice的createRfcommSocketToServiceRecord(UUID)方法,得到一個BluetoothSocket對象。這個方法會初始化一個鏈接到BluetoothDevice對象的BluetoothSocket對象。傳遞給這個方法的UUID參數必須與服務端設備打開BluetoothServerSocket對象時所使用的UUID相匹配。在你的應用程序中簡單的使用硬編碼進行比對,若是匹配,服務端和客戶端代碼就能夠應用這個BluetoothSocket對象了。
2. 經過調用connect()方法來初始化鏈接。在這個調用中,爲了找到匹配的UUID,系統會在遠程的設備上執行一個SDP查詢。若是查詢成功,而且遠程設備接收了該鏈接請求,那麼它會在鏈接期間共享使用RFCOMM通道,而且connect()方法會返回。這個方法是一個阻塞調用。若是由於某些緣由,鏈接失敗或鏈接超時(大約在12秒以後),就會拋出一個異常。
由於connect()方法是阻塞調用,這個鏈接過程始終應該在獨立與主Activity線程以外的線程中被執行。
注意:在調用connect()方法時,應該始終確保設備沒有正在執行設備發現操做。若是是在發現操做的過程當中,那麼鏈接嘗試會明顯的變慢,而且更像是要失敗的樣子。
示例
如下是初始化藍牙鏈接線程的一個基本的例子:
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()方法來進行清理工做。這樣作會當即關閉被鏈接的套接字,並清理全部的內部資源。
Android的聯通性---Bluetooth(六)
管理鏈接
當你成功的鏈接了兩個(或更多)設備時,每個設備都有一個被鏈接的BluetoothSocket對象。這是良好的開始,由於你可以在設備之間共享數據。使用BluetoothSocket對象來傳輸任意數據的過程是簡單的:
1. 分別經過getInputStream()和getOutputStream()方法來得到經過套接字來處理傳輸任務的InputStream和OutputStream對象;
2. 用read(byte[])和write(byte[])方法來讀寫流中的數據。
固然,有更多實現細節要考慮。首先和前提,對於全部數據流的讀寫應該使用專用的線程。這是重要的,由於read(byte[])和write(byte[])方法是阻塞式調用。Read(byte[])方法在從數據流中讀取某些數據以前一直是阻塞的。write(byte[])方法一般是不阻塞的,可是對於流的控制,若是遠程設備不是足夠快的調用read(byte[])方法,而且中間緩存被填滿,那麼write(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,而後返回繼續等待流中的更多的字節。
發送輸出數據就像在主Activity中調用線程的write()方法同樣簡單,而且給該方法傳入要發送的字節。而後這個方法簡單的調用write(byte[])方法把數據發送給遠程設備。
線程的cancel()方法是相當重要的,以便該鏈接在任什麼時候候可以經過關閉BluetoothSocket對象來終止。這個方法在使用完藍牙鏈接時應該始終被調用。
使用配置來工做
從Android3.0開始,藍牙API就包含了對用藍牙配置來進行工做的支持。Bluetooth Profile是設備之間基於藍牙進行通訊的無線接口規範。其中一個示例是Hands-Free配置。對於一個鏈接到無線耳機的移動電話,移動電話和藍牙耳機都必須支持Hands-Free配置。
你可以實現BluetoothProfile接口,來編寫你本身的支持特殊藍牙配置的類。Android藍牙API提供了對如下藍牙配置的實現:
· Headset(耳機):該配置提供了對用於移動電話的藍牙耳機的支持。Android提供了BluetoothHeadset類,這個類是一個代理,它經過進程間通訊(IPC)來控制藍牙耳機服務。它包含了Bluetoot Headset和Hands-Free(v1.5)配置。BluetoothHeadset類包含了對AT命令的支持。關於AT命令的更多討論,請看「Vendor-specific AT commands」。
· A2DP:Advanced Audio Distribution Profile的縮寫。它定義如何流化高品質的音頻,並讓音頻流在經過藍牙鏈接在設備之間傳輸。Android提供了BluetoothA2dp類,它一個經過IPC來控制的藍牙A2DP服務的代理。
· Health Device:Android4.0(API Level 14)中引入了對Bluetooth Health Device Profile(HDP)的支持。它可以讓你建立一個跟支持藍牙的健康保健設備進行藍牙通訊的應用程序,這些健康保健包括:心率監護儀、血壓測量儀、體溫計、體重秤等。對應它支持的設備的列表和它們相應的設備數據規範,藍牙聯盟的網站:www.bluetooth.org。注意,這些值也是參考了ISO/IEEE11073-20601[7]規範中命名編碼規範附件中的名爲MDC_DEV_SPEC_PROFILE_*規範。
如下是使用配置進行工做的基本步驟
1. 獲取默認的適配器;
2.使用getProfileProxy()方法來創建一個與配置相匹配的配置代理對象的鏈接。在下面的示例中,配置代理對象是一個BluetoothHeadset對象實例;
3.建立一個BluetoothProfile.ServiceListener監聽器,該監聽器會在它們鏈接到服務器或中斷與服務器的鏈接時,通知BluetoothProfile的IPC客戶端。
4.在onServiceConnected()事件中,得到對配置代理對象的處理權;
5.一旦得到配置代理對象,就能夠用它來監視鏈接的狀態,並執行與配置有關的其餘操做。
例如,如下代碼片斷顯示如何鏈接一個BluetoothHeadset代理對象,以便控制Headset配置:
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);
Android的聯通性---Bluetooth(七)
健康設備配置
Android4.0(APILevel 14)中引入了對Bluetooth Health Device Profile(HDP)支持,這回讓你建立跟支持藍牙的健康設備進行藍牙通訊的應用程序,如心率監護儀、血壓測量儀、體溫計、體重秤等。Bluetooth Health API包含了BluetoothHealth、BluetoothHealthCallbackhe和BluetoothHealthAppConfiguration等類。
在使用的Bluetooth Health API中,有助於理解如下關鍵的HDP概念:
概念
介紹
Source
HDP中定義的一個角色,一個Source是一個把醫療數據(如體重、血彈、體溫等)傳輸給諸如Android手機或平板電腦等的設備,
Sink
HDP中定義的一個角色,在HDP中,一個Sink是一個接收醫療數據的小設備。在一個Android HDP應用程序中,Sink用BluetoothHealthAppConfiguration對象來表明。
Registration
指的是給特定的健康設備註冊一個Sink。
Connection
指的是健康設備和Android手機或平板電腦之間打開的通訊通道。
建立HDP應用程序
如下是建立Android HDP應用中所涉及到的基本步驟:
1. 得到BluetoothHealth代理對象的引用。
相似於常規的耳機和A2DP配置設備,必須調用帶有BluetoothProfile.ServiceListener和HEALTH配置類型參數的getProfileProxy()方法來創建與配置代理對象的鏈接,
2. 建立BluetoothHealthCallback對象,並註冊一個扮演Health Sink角色的應用程序配(BluetoothHealthAppConfiguration)。
3. 創建跟健康設備的鏈接。某些設備會初始化鏈接,在這樣的設備中進行這一個步是沒有必要的。
4. 當成功的鏈接到健康設備時,就可使用文件描述來讀寫健康設備。所接收到的數據須要使用健康管理器來解釋,這個管理器實現了IEEE 11073-xxxxx規範。
5. 完成以上步驟後,關閉健康通道,並註銷應用程序。該通道在長期被閒置時,也會被關閉。android