Android藍芽開發入坑指南
BLE簡述
藍芽是一套非常龐大複雜的協議棧,通俗的說就是一組應用於無線系統通訊的約定,各個廠家根據這個約定生產出了各種藍芽裝置。由於藍芽協議棧非常的龐大,很多廠商並不會完全實現藍芽協議棧的所有功能。Android 4.3以後藍芽開發一般都是基於低功耗藍芽BLE4.0,BLE協議是藍芽協議的子集。

BLE協議結構
上圖是BLE協議結構,還是挺複雜的,Android開發人員可能只對GATT這個詞有點印象,Android Framework幫我們遮蔽了上述大部分協議細節。Android的bluetooth包提供了BluetoothGattCallback類用來處理藍芽連線,大致的過程是:
- 掃描藍芽
- connectGatt發起連線
- 連線成功,掃描服務(Service)
- 讀取屬性(Characteristic)和描述資訊(Description)
具體的程式實現網上教程很多,這裡不作介紹了,Service、Attribute、Description之間的關係見下圖:

Service、Attribute、Description關係
藍芽模組理論上說可以有許多Profile,不過大多數藍芽模組就一個;Profile也可以包含很多Service(一般就一個),Service可以包含很多Characteristic(一般不止一個),有的藍芽包含一個讀Characteristic,一個寫Characteristic,有的藍芽讀寫共用一個Characteristic,Characteristic內部包含一個位元組陣列Value[],對於讀寫Characteristic來說Value[]就是用於收發的資料。Android Ble sdk呼叫比較麻煩,回撥太多了,主要原因就在這裡,藍芽協議層級實在太多了。
BLE開發注意點
Android BLE開發過程中遇到的許多問題,嚴格來說並不都是Android本身的問題,很多問題來自作為通訊目標的藍芽模組,這些問題都需要開發人員注意:
訊號干擾
藍芽和WIFI都具有在2.4GHz通道通訊的能力,某些Android裝置藍芽與Wifi訊號會衝突,干擾特別嚴重,具體表現為藍芽連線成功後收發資料時丟包和超時。這個問題如果出現了,幾乎沒有什麼解決方案,只能在上層應用做好容錯處理。
藍芽初始化崩潰
這個問題存在於Android4.*的裝置,藍芽掃描裝置後會往本地儲存掃描記錄,但是這個記錄有上限,大概是500條,也就是說有搜尋過500個不同的藍芽裝置後,手機再呼叫藍芽掃描就會崩潰,系統設定裡藍芽掃描也會崩。這個問題影響不大,因為正常使用者很難遇到,而且升級到Android 5.0以上就不會出現了。
連線間隔
基本上絕大多數Android藍芽問題都是因為連線間隔導致的,那麼連線間隔是什麼呢?
藍芽通訊是基於電磁波的全雙工通訊,藍芽通訊的雙方實際就是在不停的發出廣播訊號。廣播訊息意味著所有的裝置都能收到訊息,那麼怎麼確認每個訊息是誰發給誰的呢?所以藍芽通訊前要先建立藍芽連線,建立藍芽連線簡單理解的話,其實就是雙方協商一致,以同一的步調收發資料,這個收發訊號的間隔時間稱為連線間隔。IOS藍芽的連線間隔是20ms,直觀的感受就是蘋果藍芽傳輸比較快;而Android藍芽的連線時隔各廠商都不一樣,有40ms、50ms、70ms等等,都比IOS要大一些。還有非常其怪的:華為2018年以後生產的很多手機型號,正常藍芽通訊時是40ms,但如果同時手機正在進行視訊聊天,藍芽連線間隔就會立即增加到70ms。
藍芽模組每個連線間隔內收發的資料包有效載荷最大是20位元組,寫資料時如果往Characteristic裡寫資料20個位元組,多餘的資料會丟棄。假設連線間隔是40ms,那麼藍芽模組傳輸速率就是1000/40*20=500B/s,實在太慢了,BLE協議不適合收發大量資料。
連線間隔本來是一個非常底層的通訊概念,Android開發時無法修改連線間隔。按理說開發APP應該不用關心連線間隔才對,就好像往磁碟上儲存檔案不用關心磁碟的轉速一樣,但是通訊出錯排查問題排查到這兒了,我們又不能不重視,這就有點像路由器的MTU一樣,有時還真會導致伺服器BUG。
連線間隔導致的問題一般都是效率問題,收發資料量太大,資料還沒傳完就超時了,連線間隔是系統控制的,無法修改,所以這個問題沒有什麼解決方案,只能在軟體設計上做好容錯處理,給資料傳輸留足時間。
連線間隔還會導致一些其怪的丟包問題,這種問題很難排查,好在有藍芽協議分析儀可以去監聽藍芽訊號,不過監聽出來了,也沒什麼解決辦法,如果是自研硬體的話,只能在硬體上下點功夫,Android上沒有可修改的餘地。
BLE與GPS
嚴格來說BLE與GPS真是一點關係都沒有,但是Android很出人意料地將這倆聯絡在了一起,掃描藍芽裝置必須要申請定位許可權!硬說的話,可能是因為藍芽掃描能夠獲取訊號強度,有了訊號強度就可以推算距離,間接的就實現了定位吧!Android Framework其實也提供了不需要定位許可權的掃描方法,其實就是非BLE的普通藍芽掃描,但是這樣無法獲取scanRecord,也就是說無法得到BLE硬體的service uuid以及廣播出來的資料,只能得到目標裝置名字,幾乎沒法用。
另外,申請定位許可權了並不表示就一定能掃描藍芽了,有的Android裝置必須要開啟GPS定位的開關,否則就是掃描不出來,讓人摸不著頭腦。有時候Logcat裡都系統日誌已經打出來藍芽掃描記錄了,但是就是不觸發LeScanCallback的回撥,這種現象經常出現在vivo手機上。
配對問題
藍芽協議提供了配對機制,其實就是讓藍芽記住目標裝置,下次連線免去了掃描的步驟。其實掃描和配對都不是必須的,只要知道藍芽裝置的MAC地址,就可以直接發起藍芽連線了(IOS有所差別,只能得到UUID,無法得到MAC地址)。但可能是出於安全考慮,配對之後的裝置,藍芽建立連線時會發起加密申請,詢問目標裝置是否支援加密通訊,哪果目標裝置不支援加密又不知道怎麼應答,那連線就掛掉了,自研藍芽裝置時需要注意一下。
藍芽開關問題
Android可以通過程式開啟、關閉藍芽,但是開啟藍芽需要動態許可權申請,這倒是小事,互動設計時給個對話方塊和使用者說一下就好了。開啟藍芽的操作即時返回結果的,而開啟藍芽是需要時間的,非同步處理,大概幾十毫秒,所以enable()後立即掃描藍芽往往什麼也搜不到。這個注意一下就好了,程式碼里加個延時不費事。一個特別要注意的事情,藍芽enable()和disable()切記不能頻繁操作,否則手機藍芽會出現一現難琢磨的現象,甚至會被搞宕機…而且實踐發現,程式碼裡enable()和在藍芽設定裡手動開啟藍芽是不一樣的,有的時候enable()後藍芽死活連不上,但是在系統設定裡重新關閉、開啟藍芽就正常了。總之enable()要謹慎使用,有的手機(三星)特嬌貴,藍芽宕機了重啟手機都不好使。
Android與IOS對比
IOS藍芽比Android藍芽要穩定的多,畢竟型號單一嘛。在開發層面,IOS藍芽不會顯示裝置MAC地址,其實對使用者來說一般不會有檢視藍芽MAC地址的需求。藍芽效能上,除了傳輸效率高,IOS藍芽建立連線也比Android順暢得多,尤其是藍芽模組Characteristic很多的時候Android連線時discoverServices()會很慢。
企業採購藍芽模組千萬不能只用iphone做驗證,IOS藍芽實在比Android好太多。IOS上除錯藍芽可以用非常著名的LightBlue,體驗非常棒;相應的Android上可以用nRF-Connect,體驗差點兒,不過該有的功能都有的。
總結
個人實踐來看,Android藍芽模組穩定性並不高,和4G、wifi模組相比差很遠,這其實是使用者需求決定的,手機藍芽確實很少使用,因而手機廠商對藍芽模組的重視程度也不高,我們多次發現即使同一型號的手機,藍芽效能也會天差地別,有的連線很穩定,有的丟包特別嚴重。我們猜測可能手機代工不一樣,或者藍芽模組不是採購自同一供應商。
藍芽協議之所以被很多廠商所接受,就是因其功能很完善,實現靈活性很高,藍芽模組只要實現藍芽協議規定的功能即可,而程式碼如何寫這個就是廠商自己的事情了,所以肯定會有相容效能問題,相容性問題總結下來就三個:掃描不到、連線不上、收不到資料,企業採購藍芽模組一定要做好驗收工作。
Anroid藍芽給開發者的印象多是“不靠譜”,但是用於小規模、短時間資料傳輸還是非常方便的,比如用於共享單車開鎖、智慧手環等等。如果需要大規模資料傳輸的話還是wlan比較靠譜,比如智慧攝像頭、無人機都是採用wlan傳輸資料,有的智慧硬體會把藍芽與wifi模組相結合,比如通過藍芽設定wlan密碼接入wifi網路。藍芽具有的功耗低、體積小的特點,這是很多無線通訊方式無法批擬的,因此藍芽在物聯網領域還是大有可為的,開發者如何把充分利用手機藍芽,我們還需要不斷探索。