1. 程式人生 > >低功耗藍芽 (BLE)開發各種坑

低功耗藍芽 (BLE)開發各種坑

這段時間在做低功耗藍芽 (BLE) 應用的開發(並不涉及藍芽協議棧)。總體感覺 Android BLE 還是不太穩定,開發起來也是各種痛苦。這裡記錄一些雜項和開發中遇到的問題及其解決方法,避免大家踩坑。本文說的問題有些沒有得到官方文件的驗證,不過也有一些論壇帖子的支援,也可以算是有一定根據。

  1. Android 從 4.3(API Level 18) 開始支援低功耗藍芽,但是隻支援作為中心裝置 (Central) 模式,這就意味著 Android 裝置只能主動掃描和連結其他外圍裝置 (Peripheral)。從Android 5.0(API Level 21)開始兩種模式都支援。BLE 官方文件在  

    這裡 。 

  2. 在  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() 儘快返回。  [  參考帖子 ] 

  3. 在使用  BluetoothDevice.connectGatt() 或者 BluetoothGatt.connect() 等建立  BluetoothGatt 連線的時候,在任何時刻都只能最多一個裝置在嘗試建立連線。如果同時對多個藍芽裝置發起建立 Gatt 連線請求。如果前面的裝置連線失敗了,後面的裝置請求會被永遠阻塞住,不會有任何連接回調。 

    開發建議:如果要對多個裝置發起連線請求,最好是有一個同一個的裝置連線管理,把發起連線請求序列化起來。前一個裝置請求建立連線,後面請求在佇列中等待。如果連線成功了,就處理下一個連線請求。如果連線失敗了(例如出錯,或者連線超時失敗),就馬上呼叫  BluetoothGatt.disconnect() 來釋放建立連線請求,然後處理下一個裝置連線請求。  [  參考帖子 ] 

  4. 對 BluetoothGatt 操作  (read/write)Characteristic() , (read/write)Descriptor() 和  readRemoteRssi() 都是非同步操作。需要特別注意的是,同時只能有一個操作(有些貼這說只能同時有一個 writeCharacteristic() ,這個我並沒有嚴格驗證),也就是等上一個操作回撥(例如  onCharacteristicWrite() )以後,再進行下一個操作。 

    開發建議:把這寫操作都封裝成同步操作,一個操作回撥之前,阻塞主其他呼叫。  [  參考帖子 ] 

  5. BLE 裝置的建立和斷開連線的操作,例如 BluetoothDevice.connectGatt() ,  BluetoothGatt.connect() , BluetoothGatt.disconnect() 等操作最好都放在主執行緒中,否則你會遇到很多意想不到的麻煩。 

    開發建議:對  BluetoothGatt 的連線和斷開請求,都通過傳送訊息到 Android 的主執行緒中,讓主執行緒來執行具體的操作。例如建立一個  new Handler(context.getMainLooper()); ,把訊息傳送到這個  Handler中。  [  參考帖子 ] 

  6. 如果你在開發 BLE 應用的時候,有時候會發現系統的功耗明顯增加了,檢視電量使用情況,藍芽功耗佔比非常高,好像低功耗是徒有虛名。使用  adb bugreport 獲取的了系統資訊,分析發現一個名叫 BluetoothRemoteDevices 的  WakeLock 鎖持有時間非常長,導致系統進入不了休眠。分析原始碼發現,在連線 BLE 裝置的過程中,系統會持有 (Aquire) 這個  WakeLock ,直到連線上或者主動斷開連線(呼叫  disconnect() )才會釋放。如果BLE裝置不在範圍內,這個超時時間大約為30s,而這時你可能又要嘗試重新連線,這個  WakeLock 有被重新持有,這樣系統就永遠不能休眠了。 

    開發建議:對BLE裝置連線,連線過程要儘量短,如果連線不上,不要盲目進行重連,否這你的電池會很快被消耗掉。這個情況,實際上對傳統藍芽裝置連線也是一樣。  [  參考帖子 ] 

  7. Android 作為中心裝置,最多隻能同時連線 6 個 BLE 外圍裝置(可能不同的裝置這個數字不一樣),超過 6 個,就會連線不上了。現在 BLE 裝置越來越多,其實並不夠用,所以在開發的過程中,需要特別的謹慎使用。

    開發建議:按照需要連線裝置,如果裝置使用完了,應該馬上釋放連線(呼叫 BluetoothGatt.close() ),騰出系統資源給其他可能的裝置連線。  [  參考帖子 ] 

本文只是一些經驗之談,觀點也比較瑣碎。這裡很多問題都看起來是藍芽協議棧不完善導致的,或許在後面 Android 升級中會修復這些問題,我這裡說的可能不適用了。

From: http://www.tuicool.com/articles/aqyyayZ