這段時間在作低功耗藍牙 (BLE) 應用的開發(並不涉及藍牙協議棧)。整體感受 Android BLE 仍是不太穩定,開發起來也是各類痛苦。這裏記錄一些雜項和開發中遇到的問題及其解決方法,避免你們踩坑。本文說的問題有些沒有獲得官方文檔的驗證,不過也有一些論壇帖子的支持,也能夠算是有必定根據。html
Android 從 4.3(API Level 18) 開始支持低功耗藍牙,可是隻支持做爲中心設備 (Central) 模式,這就意味着 Android 設備只能主動掃描和連接其餘外圍設備 (Peripheral)。從Android 5.0(API Level 21)開始兩種模式都支持。BLE 官方文檔在 這裏 。 android
在 BluetoothAdapter.startLeScan()
的時候,在 BluetoothAdapter.LeScanCallback.onLeScan()
中不能作太多事情,特別是周圍的BLE設備多的時候,很是容易致使出現以下錯誤: 異步
E/GKI LINUX(17741): ##### ERROR : GKI exception: GKI exception(): Task State Table E/GKI LINUX(17741): ##### ide
E/GKI LINUX(17741): ##### ERROR : GKI exception: TASK ID [0] task name [BTU] state [1] oop
E/GKI ui
LINUX(17741): #####google
LINUX(17741): ##### ERROR : GKI spa
exception: TASK ID [1] task name [BTIF] state [1]線程
LINUX(17741): ##### code
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()
儘快返回。 [ 參考帖子 ]
在使用 BluetoothDevice.connectGatt()
或者 BluetoothGatt.connect()
等創建 BluetoothGatt
鏈接的時候,在任什麼時候刻都只能最多一個設備在嘗試創建鏈接。若是同時對多個藍牙設備發起創建 Gatt 鏈接請求。若是前面的設備鏈接失敗了,後面的設備請求會被永遠阻塞住,不會有任何鏈接回調。
開發建議:若是要對多個設備發起鏈接請求,最好是有一個同一個的設備鏈接管理,把發起鏈接請求序列化起來。前一個設備請求創建鏈接,後面請求在隊列中等待。若是鏈接成功了,就處理下一個鏈接請求。若是鏈接失敗了(例如出錯,或者鏈接超時失敗),就立刻調用 BluetoothGatt.disconnect()
來釋放創建鏈接請求,而後處理下一個設備鏈接請求。 [ 參考帖子 ]
對 BluetoothGatt 操做 (read/write)Characteristic()
, (read/write)Descriptor()
和 readRemoteRssi()
都是異步操做。須要特別注意的是,同時只能有一個操做(有些貼這說只能同時有一個 writeCharacteristic()
,這個我並無嚴格驗證),也就是等上一個操做回調(例如 onCharacteristicWrite()
)之後,再進行下一個操做。
開發建議:把這寫操做都封裝成同步操做,一個操做回調以前,阻塞主其餘調用。 [ 參考帖子 ]
BLE 設備的創建和斷開鏈接的操做,例如 BluetoothDevice.connectGatt()
, BluetoothGatt.connect()
, BluetoothGatt.disconnect()
等操做最好都放在主線程中,不然你會遇到不少意想不到的麻煩。
開發建議:對 BluetoothGatt
的鏈接和斷開請求,都經過發送消息到 Android 的主線程中,讓主線程來執行具體的操做。例如建立一個 new Handler(context.getMainLooper());
,把消息發送到這個 Handler
中。 [ 參考帖子 ]
若是你在開發 BLE 應用的時候,有時候會發現系統的功耗明顯增長了,查看電量使用狀況,藍牙功耗佔比很是高,好像低功耗是徒有虛名。使用 adb bugreport
獲取的了系統信息,分析發現一個名叫 BluetoothRemoteDevices
的 WakeLock
鎖持有時間很是長,致使系統進入不了休眠。分析源代碼發現,在鏈接 BLE 設備的過程當中,系統會持有 (Aquire) 這個 WakeLock
,直到鏈接上或者主動斷開鏈接(調用 disconnect()
)纔會釋放。若是BLE設備不在範圍內,這個超時時間大約爲30s,而這時你可能又要嘗試從新鏈接,這個 WakeLock
有被從新持有,這樣系統就永遠不能休眠了。
開發建議:對BLE設備鏈接,鏈接過程要儘可能短,若是鏈接不上,不要盲目進行重連,否這你的電池會很快被消耗掉。這個狀況,實際上對傳統藍牙設備鏈接也是同樣。 [ 參考帖子 ]
Android 做爲中心設備,最多隻能同時鏈接 6 個 BLE 外圍設備(可能不一樣的設備這個數字不同),超過 6 個,就會鏈接不上了。如今 BLE 設備愈來愈多,其實並不夠用,因此在開發的過程當中,須要特別的謹慎使用。
開發建議:按照須要鏈接設備,若是設備使用完了,應該立刻釋放鏈接(調用 BluetoothGatt.close()
),騰出系統資源給其餘可能的設備鏈接。 [ 參考帖子 ]
本文只是一些經驗之談,觀點也比較瑣碎。這裏不少問題都看起來是藍牙協議棧不完善致使的,或許在後面 Android 升級中會修復這些問題,我這裏說的可能不適用了。
From: http://www.tuicool.com/articles/aqyyayZ