Android Bluetooth 與 Headset 通信

Android Bluetooth

本文主要記錄 Android 與藍牙設備通信過程的整個流程,并對流程中的一些坑給出相應的解決思路。
本文中的通信設備是藍牙耳機,其他藍牙設備整體思路及流程類似,視具體情況稍加調整

最近手上有個項目是基于移動端 App 與藍牙耳機通信的,死磕一番發(fā)現藍牙真的是...

for (int i = 0; i < 10000; i ++) {
    fuck("Android Bluetooth");
}

下圖是項目完成后整理的一份流程表,希望對大家有幫助

Android 藍牙連接流程

通過圖中所示流程相信大部分開發(fā)者都能清楚的了解到藍牙的整個連接過程,但是為什么要畫這張圖呢?是因為在圖中星標的這些位置需要引起大家注意

獲取藍牙適配器

這是所有藍牙開發(fā)的第一步

在 Android API 17 及之前的版本中,需要通過 BluetoothAdapter 的 getDefaultAdapter() 函數進行獲取

在 Android API 18 開始可以通過 BluetoothManager 這個類的 getAdapter() 函數獲取到 BluetoothAdapter 對象

判斷藍牙開關

既然要用藍牙,你得保證它開著啊

開關檢測可以通過 BluetoothAdapter 的 isEnabled() 函數進行檢測,返回 true 表示已開啟,false 未開啟,如果 BluetoothAdapter 為 null 則表示設備不支持藍牙

如果藍牙未開啟,這個時候就需要申請開啟藍牙

這里特別說明,不要使用 mBluetoothAdapter.enable() 這句代碼來開啟藍牙,在 Android 動態(tài)授權的機制出來后,這句代碼在某些高版本的系統中無效,這對用戶體驗是致命的,就好像你一拳打出去打在空氣中一樣?當然,如果你覺得用戶體驗沒個屌用就請原諒在下這頓逼逼叨

為了保證通過較好的用戶體驗來打開藍牙,可以使用如下代碼申請

// REQUEST_ENABLE_BT 是定義的局部變量,在 onActivityResult 會通過 requestCode 變量返回該值,用于識別操作類型
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);

然后在 Activity 的 onActivityResult() 函數中處理用戶的決定

掃描設備

掃描設備可以通過 BluetoothAdapter 的 startDiscovery() 開啟掃描設備,千萬要記得在發(fā)現目標設備之后立即調用 cancelDiscovery() 關閉。Google 官方也強調過掃描藍牙設備是一個比較耗的過程,所以能省則省

掃描到的設備會以廣播的形式回調給設備,所以我們需要注冊 BluetoothDevice.ACTION_FOUND 的監(jiān)聽器,在這里便可以獲取到目標設備的 BluetoothDevice 類

設備校驗

這里的設備校驗我采用的是通過名稱匹配的方式,我們的藍牙耳機出廠的設備名稱是有一定規(guī)律的,如:HeadsetHK_38913、HeadsetHK_81390、HeadsetHK_34698

那我只要找到以 HeadsetHK_ 開頭的設備就嘗試進入下面的流程

可能有些同學會覺得這里的處理不夠嚴謹,那是得看使用場景來的

如果用戶正常想要使用我們的藍牙耳機的場景下,他周圍剛好有一個名字同樣以 HeadsetHK_ 開頭的設備并且又不是我們的產品,這種概率大家自己掂量

為什么會選用這個不夠嚴謹的方式來進行校驗,是因為在此之前,我也是希望能夠藍牙設備藍牙設備能夠給我一個反饋,告訴 app 我是自己人,快拉我上車

但是通過 BluetoothSocket 去連接設備的時候發(fā)現一個問題,藍牙不穩(wěn)定,而且部分手機有些通信堵塞的感覺

在藍牙設備開機的情況下,BluetoothSocket 連接可能 3~5s 便可以進行通信,但是如果設備關機了,BluetoothSocket 連接的過程可能需要等上15~30s 才能給出反饋

當然針對不同的使用場景需要采用不同的方案,這里只是將我的思路給出來分享給大家

設備配對

在 API 19 中,BluetoothDevice 類新增了 createBond() 函數,可用于主動配對設備,但是在此之前的版本中,我們需要通過反射的機制來進行主動配對。代碼如下

// 傳入的 device 就是希望配對的 BluetoothDevice 類
Method bond = BluetoothDevice.class.getMethod("createBond");
bond.invoke(device);

連接設備

再看看上面的流程圖,為什么我要將掃描設備、設備校驗、連接設備和數據通信用星號標出?

掃描設備是因為比較耗,需要強調;設備校驗是為了提升能夠成功建立連接的準確度;

那連接設備和數據通信為什么要單獨擰出來說明?而且還分為兩個步驟?

按照我沒有了解之前,自以為連接成功就可以進行通信了,然并卵

大家記得 Android 系統自帶的藍牙設置界面中,為什么有些系統在點擊掃描到的設備列表之后,點擊目標設備第一次是設備配對,而第二次點擊才是連接設備?

因為每個藍牙設備都是由0或者多個組件構成的,這些組件有音視頻、拍照、定位等各個功能,而且每個設備都有一個主要和次要的組件

像藍牙耳機主要組件肯定就是音頻,但是你不能肯定的說市面上所有藍牙耳機都沒有定位組件

說這些是為了告訴大家,既然藍牙模塊其實是N個功能組件的集合,那我們是可以單獨與藍牙設備中的某一個功能組件建立連接的

同樣的,如果希望發(fā)起與藍牙設備的連接,也要使用反射

Method connect = btHeadsetCls.getMethod("connect", BluetoothDevice.class);
connect.setAccessible(true);
connect.invoke(bluetoothHeadset, device);

不論是配對還是連接的反射返回,都會返回一個 boolean 值告訴我們成功或者失敗,但是我可以確切的告訴大家,這兩個值是不可靠的,那這個時候我們怎么才能知道藍牙設備與我們的 App 連接成功呢?

需要監(jiān)聽 BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED,該廣播會反饋藍牙連接的當前狀態(tài)

我們只需要在收到廣播之后,作出對應的 UI 改變即可

數據通信

確保設備連接成功之后,就需要進行數據通信了

具體的通信方式這里不再贅述,主要使用到的是 BluetoothDevice 類中的 createRfcommSocketToServiceRecord() 創(chuàng)建連接然后進行讀取

這里直接分享給大家一個網上找到工具類

<a target='_blank'>BluetoothChatUtil 藍牙通信連接工具類下載</a>


后話

其實文中還有很多沒有提及到的細節(jié)。例如對藍牙廣播的監(jiān)聽是否應該放到 service 中?當用戶在下拉菜單或者是轉到系統設置界面中進行藍牙設置的時候,我們 app 是接收不到廣播的。

本文沒有對藍牙權限獲取的步驟進行說明,直接從獲取藍牙適配器開始。后面會陸續(xù)整理出相應的文章希望大家關注

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容