Android 平臺包含藍牙網絡堆棧支持,憑藉此支持,設備能以無線方式與其餘藍牙設備交換數據。應用框架提供了經過 Android Bluetooth API 訪問藍牙功能的途徑。使用 Bluetooth API Android 應用能夠執行下面的操做:java
傳統藍牙適用於電池使用強度較大的操做,例如 Android 設備之間的流傳輸和通訊等。針對具備低功耗要求的藍牙設備,Android 4.3(API 18)中引入了面向低功耗藍牙的 API 支持。android
使用 Android Bluetooth API 來完成使用藍牙進行通訊的四項主要任務:設置藍牙、查找局部區域內的配對設備或可用設備、鏈接設備,以及在設備之間傳輸數據。數據庫
關於藍牙的 API 在 android.bluetooth
包中,下面介紹一下和藍牙相關的主要類服務器
BluetoothAdapter網絡
本地藍牙適配器,是全部藍牙交互的入口點,表示藍牙設備自身的一個藍牙設備適配器,整個系統只有一個藍牙適配器。經過它能夠發現其餘藍牙設備,查詢綁定(配對)設備的列表,使用已知的 Mac 地址實例化 BluetoothDevice
以及建立 BluetoothServerSocket
用來偵聽來自其餘設備的通訊。併發
BluetoothDevice框架
表示遠程的藍牙設備。利用它能夠經過 BluetoothSocket
請求與某個遠程設備創建鏈接,或查詢有關該設備的信息,例如設備的名稱、地址、類和綁定狀態等。異步
BluetoothSocketsocket
表示藍牙套接字接口(與 TCP Socket 類似)。這是容許應用經過 InputStream
和 OutputStream
與其餘藍牙設備交換數據的節點。正是利用這個對象來完成藍牙設備間的數據交換,加密
BluetoothServerSocket
表示用於偵聽傳入請求的開發服務器套接字(相似於 TCP ServerSocket)要鏈接兩臺 Android 設備,其中一臺設備必須使用此類開發的一個服務器套接字。當一臺遠程藍牙設備向此設備發出鏈接請求時,BluetoothServerSocket
將會在接受鏈接後返回已鏈接的 BluethoothSocket
。
BluetoothClass
描述藍牙設備的通常特性和功能。這是一組只讀屬性,用於定義設備的主要和次要設備類及其服務。不過,它不能可靠地描述設備支持的全部藍牙配置文件和服務,而是適合做爲設備類型提示。
BluetoothProfile
表示藍牙配置文件的接口。藍牙配置文件是適用於設備間藍牙通訊的無線接口規範。免提配置文件即是一個示例。如需瞭解關於配置文件的詳細討論,參考下面配置文件的講解
BluetoothHeadset
提供藍牙耳機支持,以便與手機配合使用。其中包括藍牙耳機和免提(1.5版)配置文件。BluetoothProfile 的實現類
BlutoothA2dp 定義高質量音頻如何經過藍牙鏈接和流式傳輸,從一臺設備傳輸到另外一臺設備。「A2DP」表明高級音頻分發配置文件。是 BluetoothProfile 的實現類
BluetoothHealth 表示用於控制藍牙服務的健康設備配置文件代理。 BluetoothProfile 的實現類。
BluetoothGatt
BluetoothProfile 的實現類。與低功耗藍牙通訊有關的配置文件代理
BluetoothHealthCallback 用於實現 BluetoothHealth 回調的抽象類。必須擴展此類並實現回調方法,以接收關於應用註冊狀態和藍牙通道狀態變化的更新內容。 BluetoothHealthAppConfiguration 表示第三方藍牙健康應用註冊的應用配置,以便與遠程藍牙健康設備通訊 BluetoothProfile.ServiceListener 在 BluetoothProfile IPC 客戶端鏈接到服務(即,運行特定配置文件的內部服務)或斷開服務鏈接時向其發送通知的接口。
使用藍牙必須聲明權限 BLUETOOTH 才能夠執行藍牙通訊。
<mainifest>
<uses-permission android:name = "android.permission.BLUETOOTH"/>
<!--啓用應用啓動設備發現或者操做藍牙設備的超級管理員-->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
</mainifest>
複製代碼
getDefaultAdapter()
方法。會返回一個表示設備自身的藍牙適配器(藍牙無線裝置)的 BluetoothAdapter。 整個系統有一個藍牙適配器,咱們的應用能夠經過 BluetoothAdapter 這個對象與之交互。若是 getDefaultAdapter()
返回 null 則說明該設備不支持藍牙。BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null){
// 說明此設備不支持藍牙操做
}
複製代碼
isEnabled()
。返回 false 則說明藍牙處於關閉狀態。請求啓用藍牙。使用 ACTION_REQUEST_ENABLE
操做 Intent 調用 startActivityForResult()
將經過系統設置發出啓用藍牙的請求。也能夠經過 mBluetoothAdapter.enable()
直接打開藍牙。// 沒有開始藍牙
if(!mBluetoothAdapter.isEnabled()){
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent,REQUEST_ENBLE_BT);
}
複製代碼
咱們的應用也能夠選擇偵聽 ACTION_STATE_CHANGED
廣播 Intent。每當藍牙狀態發生變化時,系統會廣播此 Intent。此廣播包含額外字段 EXTRA_STATE
和 EXTRA_PREVIOUS_STATE
分別表示新的和舊的藍牙狀態。
使用 BluetoothAdapter 能夠經過設備發現或經過查詢配對設備的列表來查找遠程藍牙設備。 設備發現是一個掃描過程,它會搜索局部區域內已啓用藍牙功能的設備,而後請求一些關於各臺設備的信息。這個過程也稱爲發現、查詢、掃描。局部區域內的藍牙設備僅在其當前已啓用可檢測性時纔會響應發現請求。若是設備能夠檢測到,它將經過共享一些信息(例如設備名稱、類及其惟一MAC地址)來響應發現請求。利用此信息,執行發現的設備能夠選擇發起到被發現設備的鏈接。
在首次與遠程設備創建鏈接後,將會自動向用戶顯示配對請求。設備完成配對後,將會保存關於該設備的基本信息(如 設備名稱、MAC 地址)。而且可使用 Bluetooth API 讀取這些信息。利用遠程設備的已知 Mac 地址能夠隨時向其發起鏈接,而不需執行發現操做(假定該設備處於有效範圍內)。
被配對和被鏈接之間存在差異。**被配對意味着兩臺設備知曉彼此的存在,具備可用於身份驗證的共享鏈路密鑰,而且可以與彼此創建加密鏈接。被鏈接意味着設備當前共享一個 RFCOMM 通道,而且可以向彼此傳輸數據。**當前的 Android Bluetooth API 要求對設備進行配對,而後才能創建 RFCOMM 鏈接(在使用 Bluetooth API 發起加密鏈接時,會自動執行配對)。Android 設備是默認處於不可檢測狀態的。
在執行設備發現以前,有必要查詢已配對的設備集合。用來了解設備是否處於已知狀態。經過 getBondedDevices()
來實現,這將返回表示已配對設備的一組 BluetoothDevice
。
例如:咱們能夠查詢全部已配對的設備,而後使用 ArrayAdapter 向用戶顯示每臺設備的名稱:
Set<BluetoothDevice> pairedDevices = mBlutooothAdapter.getBondedDevices();
if(pairedDevices.size() > 0){
for(BluetoothDevice device:pairedDevices){
// 把名字和地址取出來添加到適配器中
mArrayAdapter.add(device.getName()+"\n"+ device.getAddress());
}
}
複製代碼
要發起鏈接僅須要知道目標藍牙設備的 Mac 地址就能夠了。
發現設備使用 startDiscovery()
該進程爲異步進程。該方法會馬上返回一個布爾值,指示是否已成功啓動發現操做。發現進程一般包含約 12 秒的查詢掃描,以後對發現的設備進行掃描,以檢索其藍牙設備的名字。
咱們的應用必須針對 ACTION_FOUND
Intent 註冊一個 BroadcastReceiver,以便接受每臺發現的設備的信息。針對每臺設備,系統會廣播 ACTION_FOUND Intent。這個 Intent 會攜帶額外的字段 EXTRA_DEVICE 和 EXTRA_CLASS。這二者分別包含 BluetoothDevice 和 BluetoothClass。
// 建立一個接受 ACTION_FOUND 的 BroadcastReceiver
private final BroadcastReceiver mReceiver = new BroadcastReceiver(){
public void onReceive(Context context,Intent intent){
String action = intent.getAction();
// 當 Discovery 發現了一個設備
if(BluetoothDevice.ACTION_FOUND.equals(action)){
// 從 Intent 中獲取發現的 BluetoothDevice
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// 將名字和地址放入要顯示的適配器中
mArrayAdapter.add(device.getName + "\n" + device.getAddress());
}
}
};
// 註冊這個 BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReiver,filter);
// 在 onDestroy 中 unRegister
複製代碼
注意 執行 discovery 對於藍牙適配器來講是一個很是繁重的過程,而且會消耗大量資源。在找到要鏈接的設備後,要確保使用 cancelDiscovery()
來中止發現,而後嘗試鏈接。若是您已經和某臺設備進行鏈接,那麼這個時候執行發現操做會大幅度的減小此鏈接可用的帶寬!所以不該該在處於鏈接狀態的時候執行發現操做!
若是咱們但願咱們的設備是能夠被其餘設備檢測到的,可使用 ACTION_REQUEST_DISCOVERABLE
來操做 Intent 調用 startActivityForResult(Intent,int)
。這樣會經過系統設置發出啓用可檢測到模式的請求(無需中止咱們的應用)。默認狀況下,設備會變爲可檢測狀態而且持續 120 秒鐘。咱們還能夠經過 EXTRA_DISCOVERABLE_DURATION
Intent Extra **來定義不一樣的持續時間。能夠設置的最大持續時間爲 3600 秒。**值爲 0 表示始終能夠被檢測到。任何小於 0 或者大於 3600 的值都會自動設置爲 120 秒鐘。
例如:
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
startActivityForResult(discoverableIntent);
複製代碼
將顯示對話框,請求用戶容許將設備設爲可檢測到。若是用戶響應爲 YES,則設備將變爲可檢測到並持續指定的時間量。而後您的 Activity 將會收到對 onActivityResult() 回調的調用,其結果代碼等於設備可檢測到的持續時間。若是用戶響應 NO 或者出現錯誤,結果代碼爲 RESULT_CANCELED
若是設備沒有打開藍牙,則啓用設備可檢測性的時候會自動啓用藍牙。
設備將在分配的時間內以靜默方式保持可檢測到模式。若是咱們但願在可檢測到模式發生變化時收到通知,能夠利用 ACTION_SCAN_MODE_CHANGED
Intent 註冊 BroadcastReceiver。它將包含額外字段 EXTRA_SCAN_MODE 和 EXTRA_PREVIOUS_SCAN_MODE,二者分別告訴咱們新的和舊的掃描模式。每一個字段可能包括SCAN_MODE_CONNECTABLE_DISCOVERABLE(可檢測到模式)、SCAN_MODE_CONNECTABLE(未處於可檢測模式但能夠接受鏈接)、SCAN_MODE_NOE(未處於可檢測到模式而且沒法鏈接)
要在兩臺設備上的應用之間建立鏈接,必須同時實現服務端和客戶端機制,由於其中一臺設備必須開放服務器套接字,而另外一臺設備必須發起鏈接(使用服務器設備的 MAC 地址發起鏈接)。當服務器和客戶端在同一 RFCOMM 通道上分別擁有已鏈接的 BluetoothSocket 時,兩者將被視爲彼此鏈接。在這種狀況下每臺設備都能得到輸入和輸出流式傳輸,而且能夠開始傳輸數據。
服務端和客戶端分別以不一樣的方式來得到 BluetoothSocket 。服務器將在傳入鏈接被接受時收到套接字。客戶端將在其打開到服務器的 RFCOMM 通道時收到該套接字。
一種實現方式是自動將每臺設備準備爲一個服務器,從而使每臺設備開發一個服務器套接字並偵聽鏈接。而後任一設備能夠發起與另外一臺設備的鏈接,併成爲客戶端。或者其中一臺設備可顯示「託管」鏈接並按需開放一個服務器套接字,從而另外一臺設備則直接發起鏈接。
在鏈接以前若是兩個設備沒有配對,則系統會自動發出配對請求
當鏈接兩臺設備時,其中一臺必須保持開發的 BluetoothServerSocket
來充當服務器,用於監聽傳入的鏈接請求,在接受了請求後提供一個已經鏈接的 BluetoothSocket
。從 BluetoothServerSocket
鏈接獲取 BluetoothSocket
後就能夠調用 close 來關閉這個等待了。
通用惟一標識符(UUID),用於表示惟一標識信息的字符串ID,128位。可使用網絡上衆多的隨機 UUID 生成器,而後經過 formString(String)
來初始化一個 UUID。
服務器套接字接受鏈接的基本過程
經過 listenUsingRfcommWithServiceRecord(String,UUID)獲取 BluetoothServerSocket
字符串是咱們本身定義的服務的可識別名稱,能夠直接使用包名。系統會自定將其寫入到設備上的新服務發現協議(SDP)數據庫條目中。UUID 也在 SDP 中,做爲與客戶端設備鏈接協議的匹配規則。只有客戶端和這裏的UUID 同樣了才能夠會被鏈接
accept()
偵聽鏈接請求
阻塞調用,將在鏈接被接受或者發生異常的時候返回,操做成功後,會返回 BluetoothSocket
。
除非要接受更多的鏈接,不然調用 close()
來關閉這個監聽
這樣會釋放服務器套接字及其全部資源,但不會關閉已經鏈接的 BluetoothSocket。與 TCP/IP 不一樣的是,RFCOMM 一次只容許每一個通道有一個已經鏈接的客戶端。
放在子線程中去執行。
例子:
private class AcceptThread extend Thread{
private final BluetoothServerSocket mServerSocket;
public AcceptThread(){
mServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);
}
public void run(){
BluetoothSocket socket = null;
while(true){
socket = mServerSocket.accept();
if(socket!=null){
// 自定義方法
manageConnectedSocket(socket);
mServerSocket.close();
break;
}
}
}
public void cancle(){
mServerSocket.close();
}
}
複製代碼
要想和保持開發服務器套接字的設備創建鏈接,必須首先要獲取該設備的 BluetoothDevice 對象。而後使用這個對象來獲取 BluetoothSocket 併發起鏈接。
客戶端鏈接的基本過程
經過 BluetoothDevice 的 createRfcommSocketToServiceRecord(UUID) 獲取 BluetoothSocket 對象
這裏的 UUID 要和服務器端的一致
經過 connect() 發起鏈接
執行此方法後,系統將會在遠程設備上執行 SDP 查找,來匹配 UUID。若是查找成功了而且遠程設備接受了該鏈接,它將共享 RFCOMM 通道在鏈接期間使用。這個時候 connect() 就會返回。這個方法也是阻塞的,若是失敗或者超時(12秒以後),將引起異常。
調用 connect() 的時候要確保客戶端沒有執行發現操做。若是執行了會大幅度下降鏈接的速度,增長失敗的可能性
例子
private class ConnectThread extend Thread{
private BluetoothDevice mDevice;
private BluetoothSocket mSocket;
public ConnectThread(BluetoothSocket device){
mDevice = device;
// 這裏的 UUID 須要和服務器的一致
mSocket = device.createRfcommSocketToServiceRecord(My_UUID);
}
public void run(){
// 關閉發現設備
mBluetoothAdapter.cancelDiscovery();
try{
mSocket.connect();
}catch(IOException connectException){
try{
mSocket.close();
}catch(IOException closeException){
return;
}
}
// 自定義方法
manageConnectedSocket(mmSocket);
}
public void cancle(){
try{
mSocket.close();
}cathc(IOException closeException){
}
}
}
複製代碼
在鏈接以前調用 cancleDiscovery()
在進行鏈接以前應該始終調用這個方法,並且調用的時候無需檢測是否正在掃描。
創建鏈接後的兩個設備都有一個 BluetoothSocket
經過這個 Socket 就能夠在這兩個設備間傳輸數據了。
過程:
從 Android 3.0 開始, Bluetooth API 便支持使用藍牙配置文件。藍牙配置文件是適用於設備間藍牙通訊的無線接口規範。
藍牙配置文件就是設備間通訊(藍牙設備)的一種規範
免提配置文件即是一個示例,對於鏈接到無線耳機的手機,兩臺設備都必須支持免提配置文件。咱們也能夠經過實現接口 BluetoothProfile
來寫入本身的類來支持特定的藍牙配置文件。Android API 提供瞭如下的幾種藍牙配置文件的實現:
使用配置文件的基本步驟
getProfileProxy()
,創建到配置文件所關聯的配置文件代理對象的鏈接。BluetoothProfile.ServiceListener
。這個監聽會在客戶端鏈接到服務或者斷開服務鏈接的時候發送通知。onServiceConnected()
中獲取配置文件代理對象的句柄。例子: 如何鏈接到 BluetoothHeadset 代理對象,以便可以控制耳機配置文件:
BluetoothHeadset mBluetoothHeadset;
// 獲取默認藍牙適配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 設置監聽(監聽鏈接狀態)
private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener(){
public void onServiceConnected(int profile,BluetoothProfile proxy){
if(profile == BluetoothProfile.HEADSET){
mBluetoothHeadset = (BluetoothHeadset)
}
}
public void onServiceDisconnected(int profile){
if(profile == BluetoothProfile.HEADSET){
mBluetoothHeadset = null;
}
}
}
// 創建與配置文件代理的鏈接
mBluetoothAdapter.getProfileProxy(contenxt,mProfileListener,BluetoothProfile.HEADSET);
// 使用 mBluetoothHeadset 代理內部的方法
// 使用完畢後關閉
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);
複製代碼
從 Android 3.0 開始。應用能夠註冊接受耳機所發送的預約義的供應商特定 AT 命令的系統廣播(例如 Plantronics +XEVENT命令)(也就是說咱們的應用能夠接受耳機藍牙商預約義的命令)。如:應用能夠接受指示所鏈接設備的電池電量的廣播,並根據須要通知用戶或採起其餘操做。使用 ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent 建立廣播接收器,用來處理耳機供應商特定的 AT 命令。
Android 4.0 引入了對藍牙健康設備配置文件(HDP)的支持。這可使用咱們的應用使用藍牙與支持藍牙功能的健康設備進行通訊(心率檢測儀、血糖儀、溫度計、檯秤)
建立 HDP 應用:
獲取 BluetoothHealth 代理對象
與常規耳機和 A2DP 相似。使用 ServiceListener 和 HEALTH 配置文件類型參數來調用 getProfileProxy(),來和配置文件代理對象創建鏈接。
建立 BluetoothHealthCallback 並註冊充當健康聚集設備的應用配置(BluetoothHealthAPPConfiguration)
創建到健康設備的鏈接
成功鏈接到健康設備後,使用文件描述符對健康設備執行讀寫操做
完成後,關閉健康通道並取消註冊該應用,該通道長時間閒置也會關閉。
關於普通藍牙設備和普通藍牙設備之間的鏈接通訊
關於藍牙設備和藍牙儀器(藍牙耳機、電子秤等等相似產品)
這種之間的通訊是經過配置文件代理來實現的。
都有一個對應的配置文件代理類。具體的操做是經過這個對象來完成。