1. 程式人生 > >Android BLE設備藍牙通信框架BluetoothKit

Android BLE設備藍牙通信框架BluetoothKit

.net hcl sta beacon 數據 影響 依賴 Language 內存泄露

BluetoothKit是一款功能強大的Android藍牙通信框架,支持低功耗藍牙設備的連接通信、藍牙廣播掃描及Beacon解析。

關於該項目的詳細文檔請關註:https://github.com/dingjikerbo/BluetoothKit

對於剛接觸Android藍牙開發的初學者來說,會經常遇到一些奇怪的坑,我也是一路走過來的,將我遇到的一些坑總結了一下,這些坑在這個項目中都修復了,所以大家不必再費時費力去重復踩一遍。這個項目目前正在不斷更新,有什麽更好的建議大家可以隨時提出來。

藍牙掃描相關的坑

一、startDiscovery在大多數手機上是可以同時發現經典藍牙和Ble的,但是startDiscovery的回調無法返回Ble的廣播,所以無法通過廣播識別設備,且startDiscovery掃描Ble的效率比StartLeScan低很多。所以在實際應用中,還是StartDiscovery和StartLeScan分開掃,前者掃經典藍牙,後者掃低功耗藍牙。

二、startLeScan() 的時候,在onLeScan() 中不能做耗時操作,特別是周圍的BLE設備多的時候,容易導致底層堵塞。如果有耗時操作請丟到子線程中去處理。

三、有的手機會過濾設備廣播,一次掃描過程中只發一次request,如果沒有收到response就再也收不到該設備廣播回調了,解決的辦法是多掃幾次。

藍牙連接相關的坑

一、對藍牙設備的操作不能並行,只能串行,即每次都要在收到上一個操作的回調後才能繼續下一個操作。但是斷開連接例外,斷開連接要馬上closeGatt,不用等任務隊列中的其他操作了。而且要給所有正在執行或者準備執行的任務都cancel。

二、有時候藍牙協議棧出現異常可能收不到回調,所以我們要對每個操作做超時檢查,否則後面的所有操作都被阻塞了。

三、對於超時的任務,最好closeGatt,下次重新連接的時候重開一個gatt。

四、藍牙連接可能不穩定,最好支持失敗自動重試機制,尤其是連接和發現服務,因為80%的問題都發生在建立連接和發現服務的時候,而且這一塊也是最耗時的。

五、當連接建立後,可以由設備端發起更改連接間隔,這樣能加快後續發現服務以及數據讀寫的速度。有的手機discover service很慢,原因是connect interval太大了,有的手機會主動向設備發起更改connect interval,而有的手機卻不會。這樣的話connect interval相差就會很大,實踐中發現有的手機是7ms,有的手機是默認的50ms,所以發現service都要8s,甚至20s的都很尋常,這對用戶來說是無法忍受的。所以比較好的辦法是設備主動發起更改connect interval

六、同一個設備的所有操作最好都放在同一個線程串行執行,最好不要放在UI線程,雖然這些操作都是異步的,理論上來說不會耗時,但是由於涉及到跨進程,還是有可能出現ANR。另外不建議每個設備都開一個線程,設備多了會費內存也會降低性能。較好的做法是開一個線程,所有設備的操作都在該線程中發起,雖然占用同一個線程,但是每個設備各自維護自己的任務隊列。

七、藍牙操作都是異步的,回調通常都在binder線程裏執行,因為這是跨進程回調回來的。一定要註意到這一點,否則會出現一些奇怪的問題。比如writeCharacter在工作線程,但是onCharacterWrite是在binder線程,回調裏如果涉及到任務隊列的調度一定要post回工作線程中處理,否則會出現多線程造成的數據不一致問題。

八、接著第七條,在回調中post回工作線程處理時要註意不要帶句柄,而是要帶數據。比如對於onCharacteristicWrite,不要帶BluetoothGattCharacteristic,而是要帶其中的value。否則會漏掉一些數據,且最後可能收到一組重復的數據。同樣的問題在notify上也有。

九、當設備固件升級後,profile可能發生了變化,然而下次discover service的時候還是返回的舊的緩存,這樣讀寫character可能會失敗。解決辦法是固件升級後,下次連接時刷新一下該設備的緩存。當然,重啟藍牙也會刷新緩存,不過會影響到所有設備。另外有時候discoverService服務發現的不全,或者根本發現不了服務,也可以考慮清除一下緩存。

十、有的手機上discoverService可能會回調不止一次onServiceDiscover,這個要註意防禦。

十一、service不要緩存,雖然uuid什麽的可能都沒變,但是這些service都會和gatt關聯的,如果gatt變了,那service就報廢了,對這些service和character做任何讀寫操作都會出錯。所以建議每次連接上時都去discover service,不要緩存。

十二、固件升級通常是寫設備,為加快寫速度,可以在write character時指定no response標誌,實踐發現速度可以提升2~3倍。不過要註意的是即便帶了no response標誌,也不代表這種寫操作是沒有回調的,我們仍然要遵循收到上一次寫回調後才能進行下一次寫操作。提升寫速度的手段還有更改MTU和更改連接間隔,不過更改MTU硬件不一定支持,得嘗試幾次。

十三、寫的時候不要指定writeType,不指定writeType不代表writeType就是WRITE_TYPE_DEFAULT,事實上系統會自動根據property來決定writeType,如果帶PROPERTY_WRITE_NO_RESPONSE屬性,則會自動選擇WRITE_TYPE_NO_RESPONSE,否則才會選擇WRITE_TYPE_DEFAULT。

十四、打開/關閉character的notify,必須等收到onDescriptorWrite回調之後才算結束,才能開始下一個任務。

十五、設備的gatt在不用時要及時關閉,系統支持的連接句柄數是有限的,當達到上限後無法再建立新的連接了。

十六、當連接斷開後要調closeGatt釋放資源,不用調disconnect,也不要下次復用之前的gatt來reconnect,因為有的手機上重連可能會存在問題,比如重連後死活發現不了service。這種情況下,最好只要斷開連接就close gatt,下次連接時打開全新的gatt,這樣就可以發現service了。

使用

使用起來非常簡單,添加一行依賴,然後創建一個全局單例BluetoothClient,詳細接口調用方法參考上面的鏈接。

1、在build.gradle中添加依賴

compile ‘com.inuker.bluetooth:library:1.3.8‘

2、創建一個BluetoothClient,最好做成單例全局使用

BluetoothClient mClient = new BluetoothClient(context);

使用到的一些技術

**一、實現了一個
完整的跨進程異步任務隊列,支持任務超時、出錯重試及防禦隊列溢出**

二、攔截並Hook系統層藍牙Binder,實現對所有藍牙設備通信的監控,當同時連接設備數過多時會自動斷掉活躍度最低的設備

三、整個框架封裝在一個service中,可靈活指定service所在進程。

四、屏蔽了接口異步回調可能持有調用端Activity引用導致的內存泄露

五、利用動態代理自動將所有操作封閉在工作線程,所以整個框架無鎖

Android BLE設備藍牙通信框架BluetoothKit