本文主要記錄 Android 與藍(lán)牙設(shè)備通信過程的整個流程凤类,并對流程中的一些坑給出相應(yīng)的解決思路扛稽。
本文中的通信設(shè)備是藍(lán)牙耳機柴灯,其他藍(lán)牙設(shè)備整體思路及流程類似物延,視具體情況稍加調(diào)整
最近手上有個項目是基于移動端 App 與藍(lán)牙耳機通信的宣旱,死磕一番發(fā)現(xiàn)藍(lán)牙真的是...
for (int i = 0; i < 10000; i ++) {
fuck("Android Bluetooth");
}
下圖是項目完成后整理的一份流程表,希望對大家有幫助
通過圖中所示流程相信大部分開發(fā)者都能清楚的了解到藍(lán)牙的整個連接過程教届,但是為什么要畫這張圖呢响鹃?是因為在圖中星標(biāo)的這些位置需要引起大家注意
獲取藍(lán)牙適配器
這是所有藍(lán)牙開發(fā)的第一步
在 Android API 17 及之前的版本中,需要通過 BluetoothAdapter 的 getDefaultAdapter() 函數(shù)進行獲取
在 Android API 18 開始可以通過 BluetoothManager 這個類的 getAdapter() 函數(shù)獲取到 BluetoothAdapter 對象
判斷藍(lán)牙開關(guān)
既然要用藍(lán)牙案训,你得保證它開著啊
開關(guān)檢測可以通過 BluetoothAdapter 的 isEnabled() 函數(shù)進行檢測买置,返回 true 表示已開啟,false 未開啟强霎,如果 BluetoothAdapter 為 null 則表示設(shè)備不支持藍(lán)牙
如果藍(lán)牙未開啟忿项,這個時候就需要申請開啟藍(lán)牙
這里特別說明,不要使用 mBluetoothAdapter.enable()
這句代碼來開啟藍(lán)牙城舞,在 Android 動態(tài)授權(quán)的機制出來后轩触,這句代碼在某些高版本的系統(tǒng)中無效,這對用戶體驗是致命的家夺,就好像你一拳打出去打在空氣中一樣脱柱?當(dāng)然,如果你覺得用戶體驗沒個屌用就請原諒在下這頓逼逼叨
為了保證通過較好的用戶體驗來打開藍(lán)牙拉馋,可以使用如下代碼申請
// REQUEST_ENABLE_BT 是定義的局部變量榨为,在 onActivityResult 會通過 requestCode 變量返回該值,用于識別操作類型
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
然后在 Activity 的 onActivityResult() 函數(shù)中處理用戶的決定
掃描設(shè)備
掃描設(shè)備可以通過 BluetoothAdapter 的 startDiscovery() 開啟掃描設(shè)備煌茴,千萬要記得在發(fā)現(xiàn)目標(biāo)設(shè)備之后立即調(diào)用 cancelDiscovery() 關(guān)閉随闺。Google 官方也強調(diào)過掃描藍(lán)牙設(shè)備是一個比較耗的過程,所以能省則省
掃描到的設(shè)備會以廣播的形式回調(diào)給設(shè)備蔓腐,所以我們需要注冊 BluetoothDevice.ACTION_FOUND 的監(jiān)聽器矩乐,在這里便可以獲取到目標(biāo)設(shè)備的 BluetoothDevice 類
設(shè)備校驗
這里的設(shè)備校驗我采用的是通過名稱匹配的方式,我們的藍(lán)牙耳機出廠的設(shè)備名稱是有一定規(guī)律的回论,如:HeadsetHK_38913散罕、HeadsetHK_81390、HeadsetHK_34698
那我只要找到以 HeadsetHK_ 開頭的設(shè)備就嘗試進入下面的流程
可能有些同學(xué)會覺得這里的處理不夠嚴(yán)謹(jǐn)傀蓉,那是得看使用場景來的
如果用戶正常想要使用我們的藍(lán)牙耳機的場景下欧漱,他周圍剛好有一個名字同樣以 HeadsetHK_ 開頭的設(shè)備并且又不是我們的產(chǎn)品,這種概率大家自己掂量
為什么會選用這個不夠嚴(yán)謹(jǐn)?shù)姆绞絹磉M行校驗僚害,是因為在此之前硫椰,我也是希望能夠藍(lán)牙設(shè)備藍(lán)牙設(shè)備能夠給我一個反饋繁调,告訴 app 我是自己人,快拉我上車
但是通過 BluetoothSocket 去連接設(shè)備的時候發(fā)現(xiàn)一個問題靶草,藍(lán)牙不穩(wěn)定蹄胰,而且部分手機有些通信堵塞的感覺
在藍(lán)牙設(shè)備開機的情況下,BluetoothSocket 連接可能 3~5s 便可以進行通信奕翔,但是如果設(shè)備關(guān)機了裕寨,BluetoothSocket 連接的過程可能需要等上15~30s 才能給出反饋
當(dāng)然針對不同的使用場景需要采用不同的方案,這里只是將我的思路給出來分享給大家
設(shè)備配對
在 API 19 中派继,BluetoothDevice 類新增了 createBond() 函數(shù)宾袜,可用于主動配對設(shè)備,但是在此之前的版本中驾窟,我們需要通過反射的機制來進行主動配對庆猫。代碼如下
// 傳入的 device 就是希望配對的 BluetoothDevice 類
Method bond = BluetoothDevice.class.getMethod("createBond");
bond.invoke(device);
連接設(shè)備
再看看上面的流程圖,為什么我要將掃描設(shè)備绅络、設(shè)備校驗月培、連接設(shè)備和數(shù)據(jù)通信用星號標(biāo)出?
掃描設(shè)備是因為比較耗恩急,需要強調(diào)杉畜;設(shè)備校驗是為了提升能夠成功建立連接的準(zhǔn)確度;
那連接設(shè)備和數(shù)據(jù)通信為什么要單獨擰出來說明衷恭?而且還分為兩個步驟此叠?
按照我沒有了解之前,自以為連接成功就可以進行通信了随珠,然并卵
大家記得 Android 系統(tǒng)自帶的藍(lán)牙設(shè)置界面中灭袁,為什么有些系統(tǒng)在點擊掃描到的設(shè)備列表之后,點擊目標(biāo)設(shè)備第一次是設(shè)備配對牙丽,而第二次點擊才是連接設(shè)備简卧?
因為每個藍(lán)牙設(shè)備都是由0或者多個組件構(gòu)成的兔魂,這些組件有音視頻烤芦、拍照、定位等各個功能析校,而且每個設(shè)備都有一個主要和次要的組件
像藍(lán)牙耳機主要組件肯定就是音頻构罗,但是你不能肯定的說市面上所有藍(lán)牙耳機都沒有定位組件
說這些是為了告訴大家,既然藍(lán)牙模塊其實是N個功能組件的集合智玻,那我們是可以單獨與藍(lán)牙設(shè)備中的某一個功能組件建立連接的
同樣的遂唧,如果希望發(fā)起與藍(lán)牙設(shè)備的連接,也要使用反射
Method connect = btHeadsetCls.getMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(bluetoothHeadset, device);
不論是配對還是連接的反射返回吊奢,都會返回一個 boolean 值告訴我們成功或者失敗盖彭,但是我可以確切的告訴大家,這兩個值是不可靠的,那這個時候我們怎么才能知道藍(lán)牙設(shè)備與我們的 App 連接成功呢召边?
需要監(jiān)聽 BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED铺呵,該廣播會反饋藍(lán)牙連接的當(dāng)前狀態(tài)
我們只需要在收到廣播之后,作出對應(yīng)的 UI 改變即可
數(shù)據(jù)通信
確保設(shè)備連接成功之后隧熙,就需要進行數(shù)據(jù)通信了
具體的通信方式這里不再贅述片挂,主要使用到的是 BluetoothDevice 類中的 createRfcommSocketToServiceRecord() 創(chuàng)建連接然后進行讀取
這里直接分享給大家一個網(wǎng)上找到工具類
<a target='_blank'>BluetoothChatUtil 藍(lán)牙通信連接工具類下載</a>
后話
其實文中還有很多沒有提及到的細(xì)節(jié)。例如對藍(lán)牙廣播的監(jiān)聽是否應(yīng)該放到 service 中贞盯?當(dāng)用戶在下拉菜單或者是轉(zhuǎn)到系統(tǒng)設(shè)置界面中進行藍(lán)牙設(shè)置的時候音念,我們 app 是接收不到廣播的。
本文沒有對藍(lán)牙權(quán)限獲取的步驟進行說明躏敢,直接從獲取藍(lán)牙適配器開始闷愤。后面會陸續(xù)整理出相應(yīng)的文章希望大家關(guān)注