Bluetooth: 那些年我們一起踩過得坑
文章的編寫源于本周的一次分享。因為業務架構的調整與需要。我們項目擴大了項目開發上的支持,加大了人員投入。把其他項目的童鞋一塊來入藍牙開發的刀山火海來。因此,作為藍牙開發的經驗人員。安卓和iOS一起準備了這次藍牙開發的坑爹匯總分享。掉進坑了,不能一個人痛,得把隔壁也拉進來瞅瞅~
分享整理了藍牙開發期間遇到的坑。其中, 安卓踩坑部分由 @文浩 整理提供,在文章中一塊匯總記錄了。
公共坑點
1.藍牙需要在主線程中執行
藍牙的數據通訊是串行異步回調的。所有操作必須在主線程執行,且一段時間內僅支持單個設備的數據發送。類似于系統的并發操作,而不是并行。不在主線程中執行會,藍牙可能不操作或幾十秒后才執行。
iOS 坑點
1. CBCentralManager 系統藍牙管理類必須設計為單例!!!
在系統Api的文檔 和 說明上, 并沒有具體說明要求藍牙管理類要設為單例。那么為什么這里要這么強調呢。這就涉及到 iOS 藍牙系統的坑了。
Apple 其實在藍牙框架的設計上, 本來就沒有要求他是單例。這是一個iOS系統的BUG。具體原因如下,在官方社區就能找到答案。
Quesion:
Why Bluetooth sometimes reported as OFF when it's actually turned on?
Only toggling Bluetooth resolved the issue in Control Center.
Reply:
When Bluetooth is toggled in Control Center it enables blacklistMode which can be seen in the provided log. This allows devices/apps with permission to remain connected (example: Apple Watch) while disconnecting others. When toggling it back on, their is a bug that does not fully disable blacklistMode. Apps will then be told Bluetooth is still off when it isn't.
The creation of a new session while in blacklistMode seems to be part of what causes the issue so the best way we have found to mitigate the effect of this issue is to not create a new central session on Bluetooth state change. Only create a new session when your app is opened or if it is closed and re-opened. It's still possible to encounter this but it will happen far less frequently as it requires a few extra steps to manifest. Otherwise once experiencing the issue, toggle Bluetooth in Control Center once or twice and you should recover the connection.
我知道大家的都比較寶貴時間(懶),不想看著大段的文字說明,就直接貼上Google翻譯啦。
從上面可以看出,藍牙的狀態獲取上有BUG。有時需要重新關閉并激活控制中心的藍牙開關才能讓機器識別出正確的狀態。更坑的是,(劃重點)使用單例僅僅是能減少發生這個bug的頻率,而不能杜絕。所以,該有的坑,還是會有。說不定什么時候和H5聯調Api調用就遇到了這個坑爹的問題。
產品: 你這的藍牙連接有個BUG,有時狀態不對。
程序員 (黑人問號臉???): 對方不想和你說話并掏出一篇原文地址: https://forums.developer.apple.com/thread/92997
2.藍牙的系統的開關其實有兩個
第一個是在設置中的藍牙開關,第二個是下面允許新的連接。
允許新的連接和控制中心的開關相關聯。他的狀態與控制中心的快捷快關一致。
這是在iOS10 還是11(具體忘了)添加的快捷按鈕。他的作用是,暫時的關閉藍牙一天。但這在用戶認知和 APP處理上就造成了一個大坑:
我們在藍牙交互彈出時會讓用戶跳轉至設置頁。但是假如用戶只是使用控制中心的關閉一天開關。在設置中藍牙的總開關是綠色并打開的。用戶很難還需要打開允許新的連接開關才能使用。這就需要 APP 做額外的圖示提醒操作了。
3.獲取mac地址的坎坷之路
在iOS藍牙框架中,另一個大坑就是不能直接獲取藍牙的mac地址。系統以安全性的名義給你隱藏了。你只能通過兩種方案解決。
(1)硬件藍牙廣播數據段中寫入藍牙地址
這種方案 體驗好,連接快,,開發處理簡單,但需要硬件工程師的支持,在藍牙廣播中把藍牙地址給你暴露出來。但對于那些商品先行走量,先售賣后再加入APP的硬件產品就沒辦法了。為了統一性就只能走惡心的方案二了。
所以在藍牙前,最好能知會項目中的商品人員,達成這方面的共識。在商品驗貨時,要求硬件商在藍牙廣播加入藍牙地址。
(2)180A 服務的 2A23 特征值
至于第二種方案,是沒有辦法中的辦法。連接慢,處理復雜,多設備情況還容易強占連接。
在藍牙的業界標準中,其實設備中是有記錄硬件信息的服務的。我們可以從那里獲取藍牙地址。具體位置在service 180A (Device Information),characteristic 2A23。
但是這類方案是有后遺癥的,很容易觸發連接搶占的行為。要獲取特征值就必須連接藍牙。因此這種方案需要把周圍的藍牙,全部都連接一遍。慢且不說,當有兩臺手機時,A手機連接上了設備會導致B手機連接不到 。而B手機也在搜索,又導致A手機連接不上。兩個互相搶設備的連接。就會有一堆坑點情況出現。
那么,如果硬件上不但沒發藍牙,還還沒有遵守業界的藍牙標準怎么辦呢?
那就只有。。。去問神奇海螺咯 :-)
4.藍牙連接上限的限制
藍牙是有連接上限的,這個仔細想象也知道,肯定構不成坑點。最坑的是手機達到連接上限時,并不會如你所愿,只是最新的藍牙連接不上了。而是系統會報XPC錯誤。并且藍牙管理類掛掉。。。之前正常的連接也用不了了。
現在我們產品的解決方案是, 在給藍牙產品使用的 vc會持有一個記錄列表, 記錄在頁面內連接的所有藍牙設備。當他被系統回收時,斷開在當前界面連上的所有藍牙連接。
5.搜索service必須先連接設備
這個算是小坑了。主要是搜索設備需要先連接上藍牙。不然不會進行搜索。但是你強行調用接口也不會報錯。
6.搜索Services 第一次搜索可能會回調兩次,第一次為空
這是在與H5童鞋聯調的時候發現的坑。并不是比現的,取決于藍牙連接和你Api調用的時間快慢。當你攔截上藍牙后快速調用搜索服務Api,有概率第一次搜索出來的服務為空數組,然后過一小會再回調一個有服務的非空服務列表。只在藍牙處理不及的時候發生。
7.多設備連接service會觸發多次回調
這個也是搜索服務的一大坑點。當一個APP連接多個藍牙時,搜索服務一個搜索信息會回調多次代理接口。
如, APP 連接 A, B, C 三臺設備。A調用一次搜索接口,收到的可能有這些回調。發現A服務(空數組),發現A服務(有值),發現A服務(有值),發現A服務(有值)。直接回調了4次。。。空數組情況參靠第5個坑點。
8.APP 只能控制在 APP 中發現的藍牙
這個不太能算坑。在iOS系統中,會有所屬的概念。就是A app連接的設備, B App 不能處理。所以如果設備是通過系統的藍牙設置連接上的。我們的app是不能處理的。
要注意的是: 即使是同一個APP,alpha版本連接上了。Beta 版本同一個APP也是控制不了的。之前就給 QA 報過BUG, 發現是使用alpha APP連接藍牙,然后通過微信登錄跳到了Beta APP,發現控制不了設備的問題。
9.外設改變名字后不能取 name,要抓廣播包 CBAdvertisementDataLocalNameKey
藍牙設備默認是可以修改名字的,但問題是修改名字后,設備下次連接的name屬性不會變(可能系統有緩存)。建議是取廣播數據段 CBAdvertisementDataLocalNameKey的值作為判斷比較準。
10.小Tips:藍牙搜索設備Api可否重復的開關
這點就不是坑了,只是藍牙搜索時是有去不去重的開關, key 是 CBCentralManagerScanOptionAllowDuplicatesKey。
安卓坑點
1.指令過快會有藍牙粘包問題
猜測是安卓系統在藍牙接收數據的處理上有時間的設置。如果硬件發送給設備端的速度太快。前一個包可能會和后一個包粘連。
例如接收A,B,C。過快時會受到 A+B前半段, B后半段+C兩個包。時序上沒有錯誤。但包之間會粘連。包的數量也會變化。
業界上的處理方案有四種:
第一種則是要求硬件設置一個最低發送間隔。直接從源頭處理。
第二種是要求廠商發送必須沾滿20字節。這樣包被沾滿了,就不會粘連。
第三種是協議上商討一個結束位。通過結束位判斷。但是需要耗費一個字節作為結束標記。
第四種是APP進行流式處理。APP先接收數據,然后拼接進行一個一個指令的切分再處理。
2.使用藍牙功能需要定位權限
藍牙的使用是需要定位功能的。猜測是類似于WiFi的限制一樣。你能從周圍的WiFi設備信號,定位推斷出當前的位置。一次類推到藍牙。
3.部分手機需要在系統中開啟高精度定位
部分手機使用藍牙時需要打開設置的高精度定位設置才能使用。具體比例不高。
4.藍牙數據長度限制會限制20字節
系統的藍牙不會對數據做切分。需要保證每條發送數據在20字節以內。多了會被截斷。
Api 平臺化的異端問題
1.系統返回service的uudid不一致
這個是系統處理上的坑點。安卓會返回藍牙服務的32位序列。但是在iOS中,如果是以藍牙標準結尾的服務,只會返回前幾位,自動幫你切除后面標準的uuid。
2.藍牙權限申請表現不一致
在藍牙權限上,iOS智能建議用戶跳轉設置修改。而安卓能直接請求權限去控制連接藍牙。因此需要額外處理。