Android藍牙問題

Android BLE藍牙易出現問題


有問題可以加羣討論:517018699
這裏寫圖片描述

藍牙回調
Android 4.4的藍牙回調是在異步線程(不在主線程),如要藍牙回調在主線程執行更新界面操作,記得切換到主線程操作

三星手機兼容性問題
connectGatt()方法是在某些三星手機上只能在UI線程調用
備註:三星的手機connet和disconnet還有connectGatt都要在UI線程中操作

Android 新APL
Android換了一套掃描設備的API:BluetoothLeScaner.startScan(List,ScanSetings,ScanCallback)

Android 新的權限
Android 6.0動態權限,Android必須擁有定位權限才能掃描到BLE設備

連接不斷開的問題
別的BLE程序非法保留連接的設備可能會導致連接不能斷開

異步問題
讀寫Characteristic,Descriptor等幾乎所有BLE操作結果都爲異步返回,若不等待上一次操作結果返回就執行下一次操作,很可能導致操作失敗或者操作無效,onDescriptorWrite()返回的線程寫入的線程爲同一個線程,別的操作一般在不同的線程回調

設備緩存
Android會對連接過的BLE設備進行Server進行緩存,如設備升級後Services等有改動,則程序會出現通信失敗,此時就得刷新緩存,但是刷新緩存的方法沒有對外開放,只能使用反射來調用BluetoothGatt類中的refresh()方法:
“`
try {
Method localMethod = mBluetoothGatt.getClass().getMethod(“refresh”);
if (localMethod != null) {
return (Boolean) localMethod.invoke(mBluetoothGatt);
}
} catch (Exception localException) {
Log.e(“refreshServices()”, “An exception occured while refreshing device”);
}

“`

掃描設備
startScan(UUID[],BluetoothAdapter.LeScanCallback)
在Android4.4及以下手機似乎只支持16位短的UUID,不支持128位完整的UUUID。

任何出錯,超時,用完馬上調用Gatt.disconnect(),Gatt.close()。

getBtAdapter().enable()是異步,立即返回,但從off到on的過程需要一個時間所以只能監聽系統broadcast發出的intent裏的state

多次掃描藍牙,在華爲榮耀,魅族M3 NOTE中有的機型會發生多次斷開——掃描——斷開——掃描…會掃描不到設備,此時需要在斷開連接後,不能立即掃描,而是先停止掃描後,過2秒再掃描才能掃描到設備

掃描儘量不要放在主線程進行,可以放在子線程裏,不然有些機型會出現do too many work in main thread.

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

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

BLE的特徵一次讀寫最大長度20字節。

一個主設備(例如Android手機)可以同時連接多個從設備(一般爲6個,例如智能硬件。超過就連接不上了),一個從設備只能被一個主設備連接,一旦從設備連接上主設備,就停止廣播,斷開連接則繼續廣播。在任何時刻都只能最多一個設備在嘗試建立連接。如果同時對多個藍牙設備發起建立Gatt連接請求。如果前面的設備連接失敗了,則後面的設備請求會被永遠阻塞住,不會有任何連接回調。所以建議:如果要對多個設備發起連接請求,最好是一個接一個的順序同步請求管理。

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

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

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

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

Android 從 4.3(API Level 18) 開始支持低功耗藍牙,但是隻支持作爲中心設備 (Central) 模式,這就意味着 Android 設備只能主動掃描和鏈接其他外圍設備 (Peripheral)。從Android 5.0(API Level 21)開始兩種模式都支持。
BLE 官方文檔在這裏

**在BluetoothAdapter.startLeScan()時候、
在BluetoothAdapter.LeScanCallback.onLeScan()中不能做太多事情,特別是周圍的BLE設備多的時候,非常容易出現一下錯誤:**
“`
E/GKI LINUX(17741): ##### ERROR : GKI exception: GKI exception(): Task State Table E/GKI LINUX(17741): #####

E/GKI LINUX(17741): ##### ERROR : GKI exception: TASK ID [0] task name [BTU] state [1]

E/GKI

LINUX(17741): #####

LINUX(17741): ##### ERROR : GKI
exception: TASK ID [1] task name [BTIF] state [1]

LINUX(17741): #####

E/GKI LINUX(17741): ##### ERROR : GKI exception: TASK ID [2] task name [A2DP-MEDIA] state [1]

E/GKI

LINUX(17741): #####

LINUX(17741): ##### ERROR : GKI exception: GKI exception 65524 getbuf: out of buffers#####

E/GKI LINUX(17741): ##### ERROR : GKI exception:

E/GKI_LINUX(17741): * * * * * * * * * * * * * * * * * * * * * *
“`
* 開發建議:在 onLeScan() 回調中只做儘量少的工作,可以把掃描到的設備,扔到另外一個線程中去處理,讓 onLeScan() 儘快返回.

BLE設備的建立和斷開連接的操作
* 例如:BluetoothDevice.connectGatt(),BluetoothGatt.connect(),BluetoothGatt.disconnect()等操作最好都放在主線程中,否則你會遇到意想不到的麻煩**
* 開發建議:對BluetoothGatt的連接和斷開請求,都通過發送消息到Android的主線程中,讓主線程來執行具體的操作,例如創建一個handler把消息發到主線程。

如果你在開發BLE應用的時候,有時候會系統的功耗明顯增加了,查看電量使用情況,藍牙功耗佔比非常高,好像低功耗是徒有虛名。使用 adb bugreport 獲取的了系統信息,分析發現一個名叫 BluetoothRemoteDevices 的 WakeLock 鎖持有時間非常長,導致系統進入不了休眠。分析源代碼發現,在連接 BLE 設備的過程中,系統會持有 (Aquire) 這個 WakeLock ,直到連接上或者主動斷開連接(調用 disconnect() )纔會釋放。如果BLE設備不在範圍內,這個超時時間大約爲30s,而這時你可能又要嘗試重新連接,這個 WakeLock 有被重新持有,這樣系統就永遠不能休眠了。
* 開發建議:對BLE設備連接,連接過程要儘量短,如果連接不上,不要盲目進行重連,否這你的電池會很快被消耗掉。這個情況,實際上對傳統藍牙設備連接也是一樣

**Android作爲中心設備,最多同時連接6個BLE外圍設備(可能不同的設備在個數字不一樣),超過6個就會連接不上,現在BLE設備越來越多,其實並不夠用,所以開發過程中,需要特別謹慎使用
* 開發建議:按照要求連接設備,如果設備使用完了,應該馬上釋放連接(調用:BluetoothGatt.close()),騰出系統資源給其他可能的設備連接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章