Bluetooth: 那些年我們一起踩過得坑
文章的編寫源于本周的一次分享。因?yàn)闃I(yè)務(wù)架構(gòu)的調(diào)整與需要。我們項(xiàng)目擴(kuò)大了項(xiàng)目開發(fā)上的支持,加大了人員投入。把其他項(xiàng)目的童鞋一塊來入藍(lán)牙開發(fā)的刀山火海來。因此,作為藍(lán)牙開發(fā)的經(jīng)驗(yàn)人員。安卓和iOS一起準(zhǔn)備了這次藍(lán)牙開發(fā)的坑爹匯總分享。掉進(jìn)坑了,不能一個(gè)人痛,得把隔壁也拉進(jìn)來瞅瞅~
分享整理了藍(lán)牙開發(fā)期間遇到的坑。其中, 安卓踩坑部分由 @文浩 整理提供,在文章中一塊匯總記錄了。
公共坑點(diǎn)
1.藍(lán)牙需要在主線程中執(zhí)行
藍(lán)牙的數(shù)據(jù)通訊是串行異步回調(diào)的。所有操作必須在主線程執(zhí)行,且一段時(shí)間內(nèi)僅支持單個(gè)設(shè)備的數(shù)據(jù)發(fā)送。類似于系統(tǒng)的并發(fā)操作,而不是并行。不在主線程中執(zhí)行會(huì),藍(lán)牙可能不操作或幾十秒后才執(zhí)行。
iOS 坑點(diǎn)
1. CBCentralManager 系統(tǒng)藍(lán)牙管理類必須設(shè)計(jì)為單例!!!
在系統(tǒng)Api的文檔 和 說明上, 并沒有具體說明要求藍(lán)牙管理類要設(shè)為單例。那么為什么這里要這么強(qiáng)調(diào)呢。這就涉及到 iOS 藍(lán)牙系統(tǒng)的坑了。
Apple 其實(shí)在藍(lán)牙框架的設(shè)計(jì)上, 本來就沒有要求他是單例。這是一個(gè)iOS系統(tǒng)的BUG。具體原因如下,在官方社區(qū)就能找到答案。
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.
我知道大家的都比較寶貴時(shí)間(懶),不想看著大段的文字說明,就直接貼上Google翻譯啦。
從上面可以看出,藍(lán)牙的狀態(tài)獲取上有BUG。有時(shí)需要重新關(guān)閉并激活控制中心的藍(lán)牙開關(guān)才能讓機(jī)器識(shí)別出正確的狀態(tài)。更坑的是,(劃重點(diǎn))使用單例僅僅是能減少發(fā)生這個(gè)bug的頻率,而不能杜絕。所以,該有的坑,還是會(huì)有。說不定什么時(shí)候和H5聯(lián)調(diào)Api調(diào)用就遇到了這個(gè)坑爹的問題。
產(chǎn)品: 你這的藍(lán)牙連接有個(gè)BUG,有時(shí)狀態(tài)不對(duì)。
程序員 (黑人問號(hào)臉???): 對(duì)方不想和你說話并掏出一篇原文地址: https://forums.developer.apple.com/thread/92997
2.藍(lán)牙的系統(tǒng)的開關(guān)其實(shí)有兩個(gè)
第一個(gè)是在設(shè)置中的藍(lán)牙開關(guān),第二個(gè)是下面允許新的連接。
允許新的連接和控制中心的開關(guān)相關(guān)聯(lián)。他的狀態(tài)與控制中心的快捷快關(guān)一致。
這是在iOS10 還是11(具體忘了)添加的快捷按鈕。他的作用是,暫時(shí)的關(guān)閉藍(lán)牙一天。但這在用戶認(rèn)知和 APP處理上就造成了一個(gè)大坑:
我們?cè)谒{(lán)牙交互彈出時(shí)會(huì)讓用戶跳轉(zhuǎn)至設(shè)置頁。但是假如用戶只是使用控制中心的關(guān)閉一天開關(guān)。在設(shè)置中藍(lán)牙的總開關(guān)是綠色并打開的。用戶很難還需要打開允許新的連接開關(guān)才能使用。這就需要 APP 做額外的圖示提醒操作了。
3.獲取mac地址的坎坷之路
在iOS藍(lán)牙框架中,另一個(gè)大坑就是不能直接獲取藍(lán)牙的mac地址。系統(tǒng)以安全性的名義給你隱藏了。你只能通過兩種方案解決。
(1)硬件藍(lán)牙廣播數(shù)據(jù)段中寫入藍(lán)牙地址
這種方案 體驗(yàn)好,連接快,,開發(fā)處理簡單,但需要硬件工程師的支持,在藍(lán)牙廣播中把藍(lán)牙地址給你暴露出來。但對(duì)于那些商品先行走量,先售賣后再加入APP的硬件產(chǎn)品就沒辦法了。為了統(tǒng)一性就只能走惡心的方案二了。
所以在藍(lán)牙前,最好能知會(huì)項(xiàng)目中的商品人員,達(dá)成這方面的共識(shí)。在商品驗(yàn)貨時(shí),要求硬件商在藍(lán)牙廣播加入藍(lán)牙地址。
(2)180A 服務(wù)的 2A23 特征值
至于第二種方案,是沒有辦法中的辦法。連接慢,處理復(fù)雜,多設(shè)備情況還容易強(qiáng)占連接。
在藍(lán)牙的業(yè)界標(biāo)準(zhǔn)中,其實(shí)設(shè)備中是有記錄硬件信息的服務(wù)的。我們可以從那里獲取藍(lán)牙地址。具體位置在service 180A (Device Information),characteristic 2A23。
但是這類方案是有后遺癥的,很容易觸發(fā)連接搶占的行為。要獲取特征值就必須連接藍(lán)牙。因此這種方案需要把周圍的藍(lán)牙,全部都連接一遍。慢且不說,當(dāng)有兩臺(tái)手機(jī)時(shí),A手機(jī)連接上了設(shè)備會(huì)導(dǎo)致B手機(jī)連接不到 。而B手機(jī)也在搜索,又導(dǎo)致A手機(jī)連接不上。兩個(gè)互相搶設(shè)備的連接。就會(huì)有一堆坑點(diǎn)情況出現(xiàn)。
那么,如果硬件上不但沒發(fā)藍(lán)牙,還還沒有遵守業(yè)界的藍(lán)牙標(biāo)準(zhǔn)怎么辦呢?
那就只有。。。去問神奇海螺咯 :-)
4.藍(lán)牙連接上限的限制
藍(lán)牙是有連接上限的,這個(gè)仔細(xì)想象也知道,肯定構(gòu)不成坑點(diǎn)。最坑的是手機(jī)達(dá)到連接上限時(shí),并不會(huì)如你所愿,只是最新的藍(lán)牙連接不上了。而是系統(tǒng)會(huì)報(bào)XPC錯(cuò)誤。并且藍(lán)牙管理類掛掉。。。之前正常的連接也用不了了。
現(xiàn)在我們產(chǎn)品的解決方案是, 在給藍(lán)牙產(chǎn)品使用的 vc會(huì)持有一個(gè)記錄列表, 記錄在頁面內(nèi)連接的所有藍(lán)牙設(shè)備。當(dāng)他被系統(tǒng)回收時(shí),斷開在當(dāng)前界面連上的所有藍(lán)牙連接。
5.搜索service必須先連接設(shè)備
這個(gè)算是小坑了。主要是搜索設(shè)備需要先連接上藍(lán)牙。不然不會(huì)進(jìn)行搜索。但是你強(qiáng)行調(diào)用接口也不會(huì)報(bào)錯(cuò)。
6.搜索Services 第一次搜索可能會(huì)回調(diào)兩次,第一次為空
這是在與H5童鞋聯(lián)調(diào)的時(shí)候發(fā)現(xiàn)的坑。并不是比現(xiàn)的,取決于藍(lán)牙連接和你Api調(diào)用的時(shí)間快慢。當(dāng)你攔截上藍(lán)牙后快速調(diào)用搜索服務(wù)Api,有概率第一次搜索出來的服務(wù)為空數(shù)組,然后過一小會(huì)再回調(diào)一個(gè)有服務(wù)的非空服務(wù)列表。只在藍(lán)牙處理不及的時(shí)候發(fā)生。
7.多設(shè)備連接service會(huì)觸發(fā)多次回調(diào)
這個(gè)也是搜索服務(wù)的一大坑點(diǎn)。當(dāng)一個(gè)APP連接多個(gè)藍(lán)牙時(shí),搜索服務(wù)一個(gè)搜索信息會(huì)回調(diào)多次代理接口。
如, APP 連接 A, B, C 三臺(tái)設(shè)備。A調(diào)用一次搜索接口,收到的可能有這些回調(diào)。發(fā)現(xiàn)A服務(wù)(空數(shù)組),發(fā)現(xiàn)A服務(wù)(有值),發(fā)現(xiàn)A服務(wù)(有值),發(fā)現(xiàn)A服務(wù)(有值)。直接回調(diào)了4次。。。空數(shù)組情況參靠第5個(gè)坑點(diǎn)。
8.APP 只能控制在 APP 中發(fā)現(xiàn)的藍(lán)牙
這個(gè)不太能算坑。在iOS系統(tǒng)中,會(huì)有所屬的概念。就是A app連接的設(shè)備, B App 不能處理。所以如果設(shè)備是通過系統(tǒng)的藍(lán)牙設(shè)置連接上的。我們的app是不能處理的。
要注意的是: 即使是同一個(gè)APP,alpha版本連接上了。Beta 版本同一個(gè)APP也是控制不了的。之前就給 QA 報(bào)過BUG, 發(fā)現(xiàn)是使用alpha APP連接藍(lán)牙,然后通過微信登錄跳到了Beta APP,發(fā)現(xiàn)控制不了設(shè)備的問題。
9.外設(shè)改變名字后不能取 name,要抓廣播包 CBAdvertisementDataLocalNameKey
藍(lán)牙設(shè)備默認(rèn)是可以修改名字的,但問題是修改名字后,設(shè)備下次連接的name屬性不會(huì)變(可能系統(tǒng)有緩存)。建議是取廣播數(shù)據(jù)段 CBAdvertisementDataLocalNameKey的值作為判斷比較準(zhǔn)。
10.小Tips:藍(lán)牙搜索設(shè)備Api可否重復(fù)的開關(guān)
這點(diǎn)就不是坑了,只是藍(lán)牙搜索時(shí)是有去不去重的開關(guān), key 是 CBCentralManagerScanOptionAllowDuplicatesKey。
安卓坑點(diǎn)
1.指令過快會(huì)有藍(lán)牙粘包問題
猜測是安卓系統(tǒng)在藍(lán)牙接收數(shù)據(jù)的處理上有時(shí)間的設(shè)置。如果硬件發(fā)送給設(shè)備端的速度太快。前一個(gè)包可能會(huì)和后一個(gè)包粘連。
例如接收A,B,C。過快時(shí)會(huì)受到 A+B前半段, B后半段+C兩個(gè)包。時(shí)序上沒有錯(cuò)誤。但包之間會(huì)粘連。包的數(shù)量也會(huì)變化。
業(yè)界上的處理方案有四種:
第一種則是要求硬件設(shè)置一個(gè)最低發(fā)送間隔。直接從源頭處理。
第二種是要求廠商發(fā)送必須沾滿20字節(jié)。這樣包被沾滿了,就不會(huì)粘連。
第三種是協(xié)議上商討一個(gè)結(jié)束位。通過結(jié)束位判斷。但是需要耗費(fèi)一個(gè)字節(jié)作為結(jié)束標(biāo)記。
第四種是APP進(jìn)行流式處理。APP先接收數(shù)據(jù),然后拼接進(jìn)行一個(gè)一個(gè)指令的切分再處理。
2.使用藍(lán)牙功能需要定位權(quán)限
藍(lán)牙的使用是需要定位功能的。猜測是類似于WiFi的限制一樣。你能從周圍的WiFi設(shè)備信號(hào),定位推斷出當(dāng)前的位置。一次類推到藍(lán)牙。
3.部分手機(jī)需要在系統(tǒng)中開啟高精度定位
部分手機(jī)使用藍(lán)牙時(shí)需要打開設(shè)置的高精度定位設(shè)置才能使用。具體比例不高。
4.藍(lán)牙數(shù)據(jù)長度限制會(huì)限制20字節(jié)
系統(tǒng)的藍(lán)牙不會(huì)對(duì)數(shù)據(jù)做切分。需要保證每條發(fā)送數(shù)據(jù)在20字節(jié)以內(nèi)。多了會(huì)被截?cái)唷?/p>
Api 平臺(tái)化的異端問題
1.系統(tǒng)返回service的uudid不一致
這個(gè)是系統(tǒng)處理上的坑點(diǎn)。安卓會(huì)返回藍(lán)牙服務(wù)的32位序列。但是在iOS中,如果是以藍(lán)牙標(biāo)準(zhǔn)結(jié)尾的服務(wù),只會(huì)返回前幾位,自動(dòng)幫你切除后面標(biāo)準(zhǔn)的uuid。
2.藍(lán)牙權(quán)限申請(qǐng)表現(xiàn)不一致
在藍(lán)牙權(quán)限上,iOS智能建議用戶跳轉(zhuǎn)設(shè)置修改。而安卓能直接請(qǐng)求權(quán)限去控制連接藍(lán)牙。因此需要額外處理。