iOS - 藍(lán)牙介紹(Bluetooth 下)

5、外設(shè)模式的使用

5.1 App 作為外設(shè)被連接的實(shí)現(xiàn)

1档玻、啟動(dòng)一個(gè) Peripheral 管理對(duì)象

打開(kāi) peripheralManager,設(shè)置 peripheralManager 的委托背犯。

2脆烟、配置本地 Peripheral,設(shè)置服務(wù)志秃、特性怔球、描述、權(quán)限等等

創(chuàng)建 characteristics洽损,characteristics 的 description庞溜,創(chuàng)建 service,把 characteristics 添加到 service 中,再把 service 添加到 peripheralManager 中流码。

當(dāng) peripheral 成功打開(kāi)后又官,才可以配置 service 和 characteristics。這里創(chuàng)建的 service 和 characteristics 對(duì)象是 CBMutableCharacteristic 和 CBMutableService漫试。他們的區(qū)別就像 NSArray 和 NSMutableArray 區(qū)別類似六敬。我們先創(chuàng)建 characteristics 和 description,description 是 characteristics 的描述驾荣,描述分很多種外构,常用的就是 CBUUIDCharacteristicUserDescriptionString。

3播掷、開(kāi)啟廣播 advertising

添加發(fā)送廣播后悔調(diào)用代理的 peripheralManagerDidStartAdvertising:error: 方法审编。

4、設(shè)置處理訂閱歧匈、取消訂閱垒酬、讀 characteristic、寫 characteristic 的委托方法

5.2 作為 Peripheral 時(shí)的請(qǐng)求響應(yīng)

5.2.1 初始化 CBPeripheralManager

將設(shè)備作為 peripheral件炉,第一步就是初始化 CBPeripheralManager 對(duì)象勘究。可以通過(guò)調(diào)用 CBPeripheralManager 的initWithDelegate:queue:options:方法來(lái)進(jìn)行初始化:

上面的幾個(gè)參數(shù)中斟冕,將 self 設(shè)為代理來(lái)接收相關(guān)回調(diào)口糕,queue 為 nil 表示在主線程。

當(dāng)你調(diào)用上面這方法后磕蛇,便會(huì)回調(diào)peripheralManagerDidUpdateState:景描。所以在此之前,你需要先遵循CBPeripheralManagerDelegate秀撇。這個(gè)代理方法能獲取當(dāng)前 iOS 設(shè)備能否作為 peripheral伏伯。

5.2.2 配置 service 和 characteristic

就像之前講到的一樣,peripheral 數(shù)據(jù)庫(kù)是一個(gè)樹形結(jié)構(gòu)捌袜。

Bluetooth24

所以在創(chuàng)建 peripheral 的時(shí)候说搅,也要像這種樹形結(jié)構(gòu)一樣,將 service 和 characteristic 裝進(jìn)去虏等。在此之前弄唧,我們需要做的是學(xué)會(huì)如何標(biāo)識(shí) service 和 characteristic。

1霍衫、使用 UUID 來(lái)標(biāo)識(shí) service 和 characteristic

service 和 characteristic 都通過(guò) 128 位的 UUID 來(lái)進(jìn)行標(biāo)識(shí)候引,Core Bluetooth 將 UUID 封裝為了 CBUUID 。關(guān)于詳細(xì) UUID 的介紹敦跌,請(qǐng)參考上面的 4.3.1 CBUUID 講解澄干。

2逛揩、為自定義的 service 和 characteristic 創(chuàng)建 UUID

你的 service 或者 characteristic 的 UUID 并沒(méi)有公共的 UUID,這時(shí)你需要?jiǎng)?chuàng)建自己的 UUID麸俘。

使用命令行的uuidgen能很容易的生成 UUID辩稽。首先打開(kāi)終端,為你的每一個(gè) service 和 characteristic 創(chuàng)建 UUID从媚。在終端輸入uuidgen然后回車逞泄,具體如下:

可以通過(guò)UUIDWithString:方法,將 UUID 生成 CBUUID 對(duì)象拜效。

3喷众、構(gòu)建 service 和 characteristic 樹形結(jié)構(gòu)

在將 UUID 打包為 CBUUID 之后,就可以創(chuàng)建 CBMutableService 和 CBMutableCharacteristic 并把他們組成一個(gè)樹形結(jié)構(gòu)了紧憾。創(chuàng)建 CBMutableCharacteristic 對(duì)象可以通過(guò)該類的initWithType:properties:value:permissions:方法:

創(chuàng)建 characteristic 的時(shí)候到千,就為他設(shè)置了 properties 和 permissions。這兩個(gè)屬性分別定義了 characteristic 的可讀寫狀態(tài)和 central 連接后是否能訂閱赴穗。上面這種初始化方式父阻,代表著 characteristic 可讀。更多的選項(xiàng)望抽,可以去看看CBMutableCharacteristic Class Reference

如果給 characteristic 設(shè)置了 value 參數(shù)履婉,那么這個(gè) value 會(huì)被緩存煤篙,并且 properties 和 permissions 會(huì)自動(dòng)設(shè)置為可讀。如果想要 characteristic 可寫毁腿,或者在其生命周期會(huì)改變它的值辑奈,那需要將 value 設(shè)置為 nil。這樣的話已烤,就會(huì)動(dòng)態(tài)的來(lái)處理 value 鸠窗。

現(xiàn)在已經(jīng)成功的創(chuàng)建了 characteristic,下一步就是創(chuàng)建一個(gè) service胯究,并將它們構(gòu)成樹形結(jié)構(gòu)稍计。調(diào)用 CBMutableService 的initWithType:primary:方法來(lái)初始化 service:

第二個(gè)參數(shù) primary 設(shè)置為 YES 表示該 service 為 primary service(主服務(wù)),與 secondary service(次服務(wù))相對(duì)裕循。primary service 描述了設(shè)備的主要功能臣嚣,并且能包含其他 service。secondary service 描述的是引用它的那個(gè) service 的相關(guān)信息剥哑。比如硅则,一個(gè)心率監(jiān)測(cè)器,primary service 描述的是當(dāng)前心率數(shù)據(jù)株婴,secondary service 描述描述的是當(dāng)前電量怎虫。

創(chuàng)建了 service 之后,就可以包含 characteristic 了:

5.2.3 發(fā)布 service 和 characteristic

構(gòu)建好樹形結(jié)構(gòu)之后,接下來(lái)便需要將這結(jié)構(gòu)加入設(shè)備的數(shù)據(jù)庫(kù)大审。這一操作 Core Bluetooth 已經(jīng)封裝好了蘸际,調(diào)用 CBPeripheralManager 的addService:方法即可:

當(dāng)調(diào)用以上方法時(shí),便會(huì)回調(diào) CBPeripheralDelegate 的peripheralManager:didAddService:error:回調(diào)饥努。當(dāng)有錯(cuò)誤捡鱼,或者當(dāng)前 service 不能發(fā)布的時(shí)候,可以在這個(gè)代理中來(lái)進(jìn)行檢測(cè):

當(dāng)你發(fā)布 service 之后酷愧,service 就會(huì)緩存下來(lái)驾诈,并且無(wú)法再修改。

5.2.4 廣播 service

搞定發(fā)布 service 和 characteristic 之后溶浴,就可以開(kāi)始給正在監(jiān)聽(tīng)的 central 發(fā)廣播了乍迄。可以通過(guò)調(diào)用 CBPeripheralManager 的startAdvertising:方法并傳入字典作為參數(shù)來(lái)進(jìn)行廣播:

上面的代碼中士败,key 只用到了 CBAdvertisementDataServiceUUIDsKey闯两,對(duì)應(yīng)的 value 是包含需要廣播的 service 的 CBUUID 類型數(shù)組。除此之外谅将,還有以下 key:

但是只有 CBAdvertisementDataLocalNameKey 和 CBAdvertisementDataServiceUUIDsKey 才是 peripheral Manager 支持的漾狼。

當(dāng)開(kāi)始廣播時(shí),peripheral Manager 會(huì)回調(diào)peripheralManagerDidStartAdvertising:error:方法饥臂。如果有錯(cuò)或者 service 無(wú)法進(jìn)行廣播逊躁,則可以在該該方法中檢測(cè):

因?yàn)榭臻g的限制,并且還可能有多個(gè) app 在同時(shí)發(fā)起廣播隅熙,所以數(shù)據(jù)廣播基于 best effort(即在接口發(fā)生擁塞時(shí)稽煤,立即丟包,直到業(yè)務(wù)量減星羝荨)酵熙。

廣播服務(wù)在程序掛起時(shí)依然可用。

5.2.5 響應(yīng) central 的讀寫操作

在連接到一個(gè)或多個(gè) central 之后驰坊,peripheral 有可能會(huì)收到讀寫請(qǐng)求匾二。此時(shí),你應(yīng)該根據(jù)請(qǐng)求作出相應(yīng)的響應(yīng)拳芙,接下來(lái)便會(huì)提到這方面的處理假勿。

1、讀取請(qǐng)求

當(dāng)收到讀請(qǐng)求時(shí)态鳖,會(huì)回調(diào)peripheralManager:didReceiveReadRequest:方法转培。該回調(diào)將請(qǐng)求封裝為了 CBATTRequest 對(duì)象,在該對(duì)象中浆竭,包含很多可用的屬性浸须。

其中一種用法是在收到讀請(qǐng)求時(shí)惨寿,可以通過(guò) CBATTRequest 的 characteristic 屬性來(lái)判斷當(dāng)前被讀的 characteristic 是哪一個(gè) characteristic:

匹配上 UUID 之后,接下來(lái)需要確保讀取數(shù)據(jù)的 offset(偏移量)不會(huì)超過(guò) characteristic 數(shù)據(jù)的總長(zhǎng)度:

假設(shè)偏移量驗(yàn)證通過(guò)删窒,下面需要截取 characteristic 中的數(shù)據(jù)裂垦,并賦值給request.value。注意肌索,offset 也要參與計(jì)算:

讀取完成后蕉拢,記著調(diào)用 CBPeripheralManager 的respondToRequest:withResult:方法,告訴 central 已經(jīng)讀取成功了:

如果 UUID 匹配不上诚亚,或者是因?yàn)槠渌驅(qū)е伦x取失敗晕换,那么也應(yīng)該調(diào)用respondToRequest:withResult:方法,并返回失敗原因站宗。官方提供了一個(gè)失敗原因枚舉闸准,可能有你需要的。

2梢灭、寫入請(qǐng)求

寫入請(qǐng)求和讀取請(qǐng)求一樣簡(jiǎn)單夷家。當(dāng) central 想要寫入一個(gè)或多個(gè) characteristic 時(shí),CBPeripheralManager 回調(diào)peripheralManager:didReceiveWriteRequests:敏释。該方法會(huì)獲得一個(gè) CBATTRequest 數(shù)組库快,包含所有寫入請(qǐng)求。當(dāng)確保一切驗(yàn)證沒(méi)問(wèn)題后(與讀取操作驗(yàn)證類似:UUID 與 offset)钥顽,便可以進(jìn)行寫入:

成功后义屏,同樣去調(diào)用respondToRequest:withResult:。但是和讀取操作不同的是耳鸯,讀取只有一個(gè) CBATTRequest,但是寫入是一個(gè) CBATTRequest 數(shù)組膀曾,所以這里直接傳入第一個(gè) request 就行:

因?yàn)槭盏降氖且粋€(gè)請(qǐng)求數(shù)組县爬,所以,當(dāng)他們其中有任何一個(gè)不滿足條件添谊,那就不必再處理下去了财喳,直接調(diào)用respondToRequest:withResult:方法返回相應(yīng)的錯(cuò)誤。

5.2.6 發(fā)送更新數(shù)據(jù)給訂閱了的 central

central 可能會(huì)訂閱了一個(gè)或多個(gè) characteristic斩狱,當(dāng)數(shù)據(jù)更新時(shí)耳高,需要給他們發(fā)送通知。下面就來(lái)詳細(xì)介紹下所踊。

當(dāng) central 訂閱 characteristic 的時(shí)候泌枪,會(huì)回調(diào) CBPeripheralManager 的peripheralManager:central:didSubscribeToCharacteristic:方法:

通過(guò)上面這個(gè)代理,可以用個(gè)數(shù)組來(lái)保存被訂閱的 characteristic秕岛,并在它們的數(shù)據(jù)更新時(shí)碌燕,調(diào)用 CBPeripheralManager 的updateValue:forCharacteristic:onSubscribedCentrals:方法來(lái)告訴 central 有新的數(shù)據(jù):

這個(gè)方法的最后一個(gè)參數(shù)能指定要通知的 central误证。如果參數(shù)為 nil,則表示想所有訂閱了的 central 發(fā)送通知修壕。

同時(shí)updateValue:forCharacteristic:onSubscribedCentrals:方法會(huì)返回一個(gè) BOOL 標(biāo)識(shí)是否發(fā)送成功愈捅。如果發(fā)送隊(duì)列任務(wù)是滿的,則會(huì)返回 NO慈鸠。當(dāng)有可用的空間時(shí)蓝谨,會(huì)回調(diào)peripheralManagerIsReadyToUpdateSubscribers:方法。所以你可以在這個(gè)回調(diào)用調(diào)用updateValue:forCharacteristic:onSubscribedCentrals:重新發(fā)送數(shù)據(jù)青团。

發(fā)送數(shù)據(jù)使用到的是通知譬巫,當(dāng)你更新訂閱的 central 時(shí),應(yīng)該調(diào)用一次updateValue:forCharacteristic:onSubscribedCentrals:壶冒。

因?yàn)?characteristic 數(shù)據(jù)大小的關(guān)系缕题,不是所有的更新都能發(fā)送成功,這種問(wèn)題應(yīng)該由 central 端來(lái)處理胖腾。調(diào)用 CBPeripheral 的readValueForCharacteristic:方法烟零,來(lái)主動(dòng)獲取數(shù)據(jù)。

5.3 請(qǐng)求響應(yīng) - 最佳實(shí)踐

5.3.1 關(guān)于廣播的思考

廣播是 peripheral 的一個(gè)重要操作咸作,接下來(lái)會(huì)講到廣播的正確姿勢(shì)锨阿。

1、注意廣播對(duì)數(shù)據(jù)大小的限制

正如前文提到過(guò)的那樣记罚,廣播是通過(guò)調(diào)用 CBPeripheralManager 的startAdvertising:方法發(fā)起的墅诡。當(dāng)你將要發(fā)送的數(shù)據(jù)打包成字典后,千萬(wàn)要記住數(shù)據(jù)大小是有限制的桐智。

即使廣播可以包含 peripheral 的很多信息末早,但是其實(shí)只需要廣播 peripheral 的名稱和 service 的 UUID 就足夠了。也就是構(gòu)建字典時(shí)说庭,填寫 CBAdvertisementDataLocalNameKey 和 CBAdvertisementDataServiceUUIDsKey 對(duì)應(yīng)的 value 即可然磷,如果使用其他 key,將會(huì)導(dǎo)致錯(cuò)誤刊驴。

當(dāng) app 運(yùn)行在前臺(tái)時(shí)姿搜,有 28 bytes 的空間可用于廣播。如果這 28 bytes 用完了捆憎,則會(huì)在掃描響應(yīng)時(shí)額外分配 10 bytes 的空間舅柜,但這空間只能用于被 CBAdvertisementDataLocalNameKey 修飾的 local name(即在startAdvertising:時(shí)傳入的數(shù)據(jù))。以上提到的空間躲惰,均不包含 2 bytes 的報(bào)文頭致份。被 CBAdvertisementDataServiceUUIDsKey 修飾的 service 的 UUID 數(shù)組數(shù)據(jù),均不會(huì)添加到特殊的 overflow 區(qū)域础拨。并且這些 service 只能被 iOS 設(shè)備發(fā)現(xiàn)知举。當(dāng)程序掛起后瞬沦,local name 和 UUID 都會(huì)被加入到 overflow 區(qū)。

為了保證在有限的空間中雇锡,正確的標(biāo)識(shí)設(shè)備和 service UUID逛钻,請(qǐng)正確構(gòu)建廣播的數(shù)據(jù)。

2锰提、只廣播必要的數(shù)據(jù)

當(dāng) peripheral 想要被發(fā)現(xiàn)時(shí)曙痘,它會(huì)向外界發(fā)送廣播,此時(shí)會(huì)用到設(shè)備的無(wú)線電(當(dāng)然還有電池)立肘。一旦連接成功边坤,central 便能直接從 peripheral 中讀取數(shù)據(jù)了,那么此時(shí)廣播的數(shù)據(jù)將不再有用谅年。所以茧痒,為了減少無(wú)線電的使用、提高手機(jī)性能融蹂、保護(hù)設(shè)備電池旺订,應(yīng)該在被連接后,及時(shí)關(guān)閉廣播超燃。停止廣播調(diào)用 CBPeripheralManager 的stopAdvertising方法即可区拳。

3、手動(dòng)開(kāi)啟廣播

其實(shí)什么時(shí)候應(yīng)該廣播意乓,多數(shù)情況下樱调,用戶比我們更清楚。比如届良,他們知道周圍沒(méi)有開(kāi)著的 BLE 設(shè)備笆凌,那他就不會(huì)把 peripheral 的廣播打開(kāi)。所以提供給用戶一個(gè)手動(dòng)開(kāi)啟廣播的 UI 更為合適士葫。

5.3.2 配置 characteristic

在創(chuàng)建 characteristic 的時(shí)候乞而,就為它設(shè)定了相應(yīng)的 properties、value 和 promissions为障。這些屬性決定了 central 如何和 characteristic 通信晦闰。properties 和 promissions 可能需要根據(jù) app 的需求來(lái)設(shè)置放祟,下來(lái)就來(lái)談?wù)勅绾闻渲?characteristic:

1鳍怨、讓 characteristic 支持通知

之前在 central 的時(shí)候提到過(guò),如果要讀取經(jīng)常變化的 characteristic 的數(shù)據(jù)跪妥,更推薦使用訂閱鞋喇。所以,如果可以眉撵,最好 characteristic 允許訂閱侦香。

如果像下面這樣初始化 characteristic 就是允許讀和訂閱:

2落塑、限制只能配對(duì)的 central 才能訪問(wèn)敏感信息

有些時(shí)候,可能有這樣的需求:需要 service 的一個(gè)或多個(gè) characteristic 的數(shù)據(jù)安全性罐韩。假如有一個(gè)社交媒體的 service憾赁,那么它的 characteristic 可能包含了用戶的姓名、郵箱等私人信息散吵,所以只讓信任的 central 才能訪問(wèn)這些數(shù)據(jù)是很有必要的龙考。

這可以通過(guò)設(shè)置相應(yīng)的 properties 和 promissions 來(lái)達(dá)到效果:

像上面這樣設(shè)置,便能只讓配對(duì)的 central 才能進(jìn)行訂閱矾睦。并且在連接過(guò)程中晦款,Core Bluetooth 還會(huì)自動(dòng)建立安全連接。

在嘗試配對(duì)時(shí)枚冗,兩端都會(huì)彈出警告框缓溅,central 端會(huì)提供 code,peripheral 端必須要輸入該 code 才能配對(duì)成功赁温。成功之后坛怪,peripheral 才會(huì)信任該 central,并允許讀寫數(shù)據(jù)束世。

6酝陈、后臺(tái)運(yùn)行藍(lán)牙服務(wù)

對(duì)于 iOS app 來(lái)說(shuō),知道現(xiàn)在是運(yùn)行在前臺(tái)和后臺(tái)是至關(guān)重要的毁涉。因?yàn)楫?dāng)程序掛起后沉帮,對(duì)資源的使用是相當(dāng)有限的。關(guān)于多任務(wù)的介紹贫堰,可以看app 開(kāi)發(fā)手冊(cè)穆壕。

默認(rèn)情況下,Core Bluetooth 是不會(huì)在后臺(tái)運(yùn)行的(無(wú)論是 central 還是 peripheral)其屏。但你也可以配置在 app 收到事件后喇勋,從掛起狀態(tài)喚醒。即使程序不是完全的支持后臺(tái)模式偎行,也可以要求在有重要事件時(shí)接收系統(tǒng)通知川背。

即使在以上兩種情況下(完全允許后臺(tái)和部分允許后臺(tái)),程序也有可能不會(huì)永遠(yuǎn)掛起蛤袒。在前臺(tái)程序需要更多內(nèi)存時(shí)熄云,被掛起的程序很有可能會(huì)被強(qiáng)制退出,那樣會(huì)斷開(kāi)所有的連接妙真。從 iOS 7 開(kāi)始缴允,能夠先保存狀態(tài)(無(wú)論是 central 還是 peripheral),并在重新打開(kāi) app 時(shí)還原這些狀態(tài)珍德。通過(guò)這一特性练般,就可以做長(zhǎng)時(shí)間操作了矗漾。

6.1 運(yùn)行在前臺(tái)的 app(Foreground-Only)

除非去申請(qǐng)后臺(tái)權(quán)限,否則 app 都是只在前臺(tái)運(yùn)行的薄料,程序在進(jìn)入后臺(tái)不久便會(huì)切換到掛起狀態(tài)敞贡。掛起后,程序?qū)o(wú)法再接收任何藍(lán)牙事件摄职。

對(duì)于 central 來(lái)說(shuō)嫡锌,掛起將無(wú)法再進(jìn)行掃描和搜索 peripheral。對(duì)于 peripheral 來(lái)說(shuō)琳钉,將無(wú)法再發(fā)起廣播势木,central 也無(wú)法再訪問(wèn)動(dòng)態(tài)變化的 characteristic 數(shù)據(jù),訪問(wèn)將返回 error歌懒。

根據(jù)不同情況啦桌,這種機(jī)制會(huì)影響程序在以下幾個(gè)方面的運(yùn)用。你正在讀取 peripheral 的數(shù)據(jù)及皂,結(jié)果程序被掛起了(可能是用戶切換到了另外一個(gè) app)甫男,此時(shí)連接會(huì)被斷開(kāi),但是要直到程序重新喚醒時(shí)验烧,你才知道被斷開(kāi)了板驳。

1、利用連接 Peripheral 時(shí)的選項(xiàng)

Foreground-Only app 在掛起的時(shí)候碍拆,便會(huì)加入到系統(tǒng)的一個(gè)隊(duì)列中若治,當(dāng)程序重新喚醒時(shí),系統(tǒng)便會(huì)通知程序感混。Core Bluetooth 會(huì)在程序中包含 central 時(shí)端幼,給用戶以提示。用戶可根據(jù)提示來(lái)判斷是否要喚醒該 app弧满。

可以利用 central 在連接 peripheral 時(shí)的方法connectPeripheral:options:中的 options 來(lái)觸發(fā)提示:

6.2 Core Bluetooth 后臺(tái)模式

如果你想讓你的 app 能在后臺(tái)運(yùn)行藍(lán)牙婆跑,那么必須在 info.plist 中打開(kāi)藍(lán)牙的后臺(tái)運(yùn)行模式。當(dāng)配置之后庭呜,收到相關(guān)事件便會(huì)從后臺(tái)喚醒滑进。這一機(jī)制對(duì)定期接收數(shù)據(jù)的 app 很有用,比如心率監(jiān)測(cè)器募谎。

下面會(huì)介紹兩種后臺(tái)模式扶关,一種是作為 central 的,一種是作為 peripheral 的近哟,如果 app 兩種角色都有驮审,那則需要開(kāi)啟兩種模式鲫寄。配置即是在 info.plist 中添加UIBackgroundModes key吉执,類型為數(shù)組疯淫,value 則根據(jù)你當(dāng)前角色來(lái)選擇:

bluetooth-central:即 Central。

bluetooth-peripheral:即 Peripheral戳玫。

這個(gè)配置在 Xcode 中熙掺,也可以在 Capabilities 中進(jìn)行配置,而不用直接面對(duì) key-value咕宿。如果要看到 key-value币绩,可以在 info.plist 中打開(kāi)查看。

1府阀、作為 Central 的后臺(tái)模式

如果在 info.plist 中配置了 UIBackgroundModes – bluetooth-central缆镣,那么系統(tǒng)則允許程序在后臺(tái)處理藍(lán)牙相關(guān)事件。在程序進(jìn)入后臺(tái)后试浙,依然能掃描董瞻、搜索 peripheral,并且還能進(jìn)行數(shù)據(jù)交互田巴。當(dāng) CBCentralManagerDelegate 和 CBPeripheralDelegate 的代理方法被調(diào)用時(shí)钠糊,系統(tǒng)將會(huì)喚醒程序。此時(shí)允許你去處理重要的事件壹哺,比如:連接的建立或斷開(kāi)抄伍,peripheral 發(fā)送了數(shù)據(jù),central manager 的狀態(tài)改變管宵。

雖然此時(shí)程序能在后臺(tái)運(yùn)行截珍,但是對(duì) peripheral 的掃描和在前臺(tái)時(shí)是不一樣的。實(shí)際情況是這樣的:

設(shè)置的 CBCentralManagerScanOptionAllowDuplicatesKey 將失效箩朴,并將發(fā)現(xiàn)的多個(gè) peripheral 廣播的事件合并為一個(gè)笛臣。

如果全部的 app 都在后臺(tái)搜索 peripheral,那么每次搜索的時(shí)間間隔會(huì)更大隧饼。這會(huì)導(dǎo)致搜索到 peripheral 的時(shí)間變長(zhǎng)沈堡。

這些相應(yīng)的調(diào)整會(huì)減少無(wú)線電使用,并提升續(xù)航能力燕雁。

2诞丽、作為 peripheral 的后臺(tái)模式

作為 peripheral 時(shí),如果需要支持后臺(tái)模式拐格,則在 info.plist 中配置 UIBackgroundModes – bluetooth-peripheral僧免。配置后,系統(tǒng)會(huì)在有讀寫請(qǐng)求和訂閱事件時(shí)捏浊,喚醒程序懂衩。

在后臺(tái),除了允許處理讀寫請(qǐng)求和訂閱事件外,Core Bluetooth 框架還允許 peripheral 發(fā)出廣播浊洞。同樣牵敷,廣播事件也有前后臺(tái)區(qū)別。在后臺(tái)發(fā)起時(shí)是這樣的:

CBAdvertisementDataLocalNameKey 將失效法希,在廣播時(shí)枷餐,廣播數(shù)據(jù)將不再包含 peripheral 的名字。

被 CBAdvertisementDataServiceUUIDsKey 修飾的 UUID 數(shù)組將會(huì)被放到 overflow 區(qū)域中苫亦,意味著只能被明確標(biāo)識(shí)了搜索 service UUID 的 iOS 設(shè)備找到毛肋。

如果所有 app 都在后臺(tái)發(fā)起廣播,那么發(fā)起頻率會(huì)降低屋剑。

6.3 巧妙的使用后臺(tái)模式

雖然程序支持一個(gè)或多個(gè) Core Bluetooth 服務(wù)在后臺(tái)運(yùn)行润匙,但也不要濫用。因?yàn)樗{(lán)牙服務(wù)會(huì)占用 iOS 設(shè)備的無(wú)線電資源唉匾,這也會(huì)間接影響到續(xù)航能力趁桃,所以盡可能少的去使用后臺(tái)模式。app 會(huì)喚醒程序并處理相關(guān)事務(wù)肄鸽,完成后又會(huì)快速回到掛起狀態(tài)卫病。

無(wú)論是 central 還是 peripheral,要支持后臺(tái)模式都應(yīng)該遵循以下幾點(diǎn):

程序應(yīng)該提供 UI典徘,讓用戶決定是否要在后臺(tái)運(yùn)行蟀苛。

一旦程序在后臺(tái)被喚醒,程序只有 10s 的時(shí)間來(lái)處理相關(guān)事務(wù)逮诲。所以應(yīng)該在程序再次掛起前處理完事件帜平。后臺(tái)運(yùn)行的太耗時(shí)的程序會(huì)被系統(tǒng)強(qiáng)制關(guān)閉進(jìn)程。

處理無(wú)關(guān)的事件不應(yīng)該喚醒程序梅鹦。

和后臺(tái)運(yùn)行的更多介紹裆甩,可以查看App Programming Guide for iOS

6.4 處理常駐后臺(tái)任務(wù)

某些 app 可能需要 Core Bluetooth 常駐后臺(tái)齐唆,比如嗤栓,一款用 BLE 技術(shù)和門鎖通信的 app。當(dāng)用戶離開(kāi)時(shí)箍邮,自動(dòng)上鎖茉帅,回來(lái)時(shí),自動(dòng)開(kāi)鎖(即使程序運(yùn)行在后臺(tái))锭弊。當(dāng)用戶離開(kāi)時(shí)堪澎,可能已超出藍(lán)牙連接范圍,所以沒(méi)辦法給鎖通信味滞。此時(shí)可以調(diào)用 CBCentralManager 的connectPeripheral:options:方法樱蛤,因?yàn)樵摲椒](méi)有超時(shí)設(shè)置钮呀,所以,在用戶返回時(shí)昨凡,可以重新連接到鎖爽醋。

但是還有這樣的情形:用戶可能離開(kāi)家好幾天,并且在這期間土匀,程序已經(jīng)被完全退出了。那么用戶再次回家時(shí)形用,就不能自動(dòng)開(kāi)鎖就轧。對(duì)于這類 app 來(lái)說(shuō),常駐后臺(tái)操作就顯得尤為重要田度。

1妒御、狀態(tài)保存與恢復(fù)

因?yàn)闋顟B(tài)的保存和恢復(fù) Core Bluetooth 都為我們封裝好了,所以我們只需要選擇是否需要這個(gè)特性即可镇饺。系統(tǒng)會(huì)保存當(dāng)前 central manager 或 peripheral manager乎莉,并且繼續(xù)執(zhí)行藍(lán)牙相關(guān)事件(即使程序已經(jīng)不再運(yùn)行)。一旦事件執(zhí)行完畢奸笤,系統(tǒng)會(huì)在后臺(tái)重啟 app惋啃,這時(shí)你有機(jī)會(huì)去存儲(chǔ)當(dāng)前狀態(tài),并且處理一些事物监右。在之前提到的 “門鎖” 的例子中边灭,系統(tǒng)會(huì)監(jiān)視連接請(qǐng)求,并在centralManager:didConnectPeripheral:回調(diào)時(shí)健盒,重啟 app绒瘦,在用戶回家后,連接操作結(jié)束扣癣。

Core Bluetooth 的狀態(tài)保存與恢復(fù)在設(shè)備作為 central惰帽、peripheral 或者這兩種角色時(shí),都可用父虑。在設(shè)備作為 central 并添加了狀態(tài)保存與恢復(fù)支持后些举,如果 app 被強(qiáng)行關(guān)閉進(jìn)程筑舅,系統(tǒng)會(huì)自動(dòng)保存 central manager 的狀態(tài)(如果 app 有多個(gè) central manager,你可以選擇哪一個(gè)需要系統(tǒng)保存)。

對(duì)于 CBCentralManager膜毁,系統(tǒng)會(huì)保存以下信息:

central 準(zhǔn)備連接或已經(jīng)連接的 peripheral

central 需要掃描的 service(包括掃描時(shí),配置的 options)

central 訂閱的 characteristic

對(duì)于 peripheral 來(lái)說(shuō)躏升,情況也差不多溯革。系統(tǒng)對(duì) CBPeripheralManager 的處理方式如下:

peripheral 在廣播的數(shù)據(jù)

peripheral 存入的 service 和 characteristic 的樹形結(jié)構(gòu)

已經(jīng)被 central 訂閱了的 characteristic 的值

當(dāng)系統(tǒng)在后臺(tái)重新加載程序后(可能是因?yàn)檎业搅艘业?peripheral),你可以重新實(shí)例化 central manager 或 peripheral 并恢復(fù)他們的狀態(tài)膳殷。

2操骡、添加狀態(tài)存儲(chǔ)和恢復(fù)支持

狀態(tài)的存儲(chǔ)和恢復(fù)功能在 Core Bluetooth 中是可選的九火,添加支持可以通過(guò)以下幾個(gè)步驟:

(必須)在初始化 central manager 或 peripheral manager 時(shí),要選擇是否需要支持册招。會(huì)在文后的【3岔激、選擇支持存儲(chǔ)和恢復(fù)】中介紹。

(必須)在系統(tǒng)從后臺(tái)重新加載程序時(shí)是掰,重新初始化 central manager 或 peripheral manager虑鼎。會(huì)在文后的【4、重新初始化 central manager 和 peripheral manager】中介紹键痛。

(必須)實(shí)現(xiàn)恢復(fù)狀態(tài)相關(guān)的代理方法炫彩。會(huì)在文后的【5、實(shí)現(xiàn)恢復(fù)狀態(tài)的代理方法】中介紹絮短。

(可選)更新 central manager 或 peripheral manager 的初始化過(guò)程江兢。會(huì)在文后的【6、更新 manager 初始化過(guò)程】中介紹丁频。

3杉允、選擇支持存儲(chǔ)和恢復(fù)

如果要支持存儲(chǔ)和恢復(fù),則需要在初始化 manager 的時(shí)候給一個(gè) restoration identifier席里。restoration identifier 是 string 類型叔磷,并標(biāo)識(shí)了 app 中的 central manager 或 peripheral manager。這個(gè) string 很重要奖磁,它將會(huì)告訴 Core Bluetooth 需要存儲(chǔ)狀態(tài)世澜,畢竟 Core Bluetooth 恢復(fù)有 identifier 的對(duì)象。

例如署穗,在 central 端寥裂,要想支持該特性,可以在調(diào)用 CBCentralManager 的初始化方法時(shí)案疲,配置 CBCentralManagerOptionRestoreIdentifierKey:

雖然以上代碼沒(méi)有展示出來(lái)封恰,其實(shí)在 peripheral manager 中要設(shè)置 identifier 也是這樣的。只是在初始化時(shí)褐啡,將 key 改成了 CBPeripheralManagerOptionRestoreIdentifierKey诺舔。

因?yàn)槌绦蚩梢杂卸鄠€(gè) CBCentralManager 和 CBPeripheralManager,所以要確保每個(gè) identifier 都是唯一的备畦。

4低飒、重新初始化 central manager 和 peripheral manager

當(dāng)系統(tǒng)重新在后臺(tái)加載程序時(shí),首先需要做的即根據(jù)存儲(chǔ)的 identifier懂盐,重新初始化 central manager 或 peripheral manager褥赊。如果你只有一個(gè) manager,并且 manager 存在于 app 生命周期中莉恼,那這個(gè)步驟就不需要做什么了拌喉。

如果 app 中包含多個(gè) manager速那,或者 manager 不是在整個(gè) app 生命周期中都存在的,那 app 就必須要區(qū)分你要重新初始化哪個(gè) manager 了尿背。你可以通過(guò)從 app delegate 中的application:didFinishLaunchingWithOptions:中取出 key(UIApplicationLaunchOptionsBluetoothCentralsKey 或 UIApplicationLaunchOptionsBluetoothPeripheralsKey)中的 value(數(shù)組類型)來(lái)得到程序退出之前存儲(chǔ)的 manager identifier 列表:

拿到這個(gè)列表后端仰,就可以通過(guò)循環(huán)來(lái)重新初始化所有的 manager 了。

5田藐、實(shí)現(xiàn)恢復(fù)狀態(tài)的代理方法

在重新初始化 manager 之后荔烧,接下來(lái)需要同步 Core Bluetooth 存儲(chǔ)的他們的狀態(tài)。要想弄清楚在程序被退出時(shí)都在做些什么汽久,就需要正確的實(shí)現(xiàn)代理方法鹤竭。對(duì)于 central manager 來(lái)說(shuō),需要實(shí)現(xiàn)centralManager:willRestoreState:回窘;對(duì)于 peripheral manager 來(lái)說(shuō)诺擅,需要實(shí)現(xiàn)peripheralManager:willRestoreState:市袖。

注意:如果選擇存儲(chǔ)和恢復(fù)狀態(tài)啡直,當(dāng)系統(tǒng)在后臺(tái)重新加載程序時(shí),首先調(diào)用的方法是centralManager:willRestoreState:或peripheralManager:willRestoreState:苍碟。如果沒(méi)有選擇存儲(chǔ)的恢復(fù)狀態(tài)(或者喚醒時(shí)沒(méi)有什么內(nèi)容需要恢復(fù))酒觅,那么首先調(diào)用的方法是centralManagerDidUpdateState:或peripheralManagerDidUpdateState:。

無(wú)論是以上哪種代理方法微峰,最后一個(gè)參數(shù)都是一個(gè)包含程序退出前狀態(tài)的字典舷丹。字典中,可用的 key 蜓肆,central 端有:

peripheral 端有:

要恢復(fù) central manager 的狀態(tài)颜凯,可以用centralManager:willRestoreState:返回字典中的 key 來(lái)得到。假如說(shuō) central manager 有想要或者已經(jīng)連接的 peripheral仗扬,那么可以通過(guò) CBCentralManagerRestoredStatePeripheralsKey 對(duì)應(yīng)得到的 peripheral(CBPeripheral 對(duì)象)數(shù)組來(lái)得到症概。

具體要對(duì)拿到的 peripheral 數(shù)組做什么就要根據(jù)需求來(lái)了。如果這是個(gè) central manager 搜索到的 peripheral 數(shù)組早芭,那就可以存儲(chǔ)這個(gè)數(shù)組的引用彼城,并且開(kāi)始建立連接了(注意給這些 peripheral 設(shè)置代理,否則連接后不會(huì)走 peripheral 的代理方法)退个。

恢復(fù) peripheral manager 的狀態(tài)和 central manager 的方式類似募壕,就只是把代理方法換成了peripheralManager:willRestoreState:,并且使用對(duì)應(yīng)的 key 即可语盈。

6舱馅、更新 manager 初始化過(guò)程

在實(shí)現(xiàn)了全部的必須步驟后,你可能想要更新 manager 的初始化過(guò)程刀荒。雖然這是個(gè)可選的操作习柠,但是它對(duì)確保各種操作能正常進(jìn)行尤為重要匀谣。假如,你的應(yīng)用在 central 和 peripheral 做數(shù)據(jù)交互時(shí)资溃,被強(qiáng)制退出了武翎。即使 app 最后恢復(fù)狀態(tài)時(shí),找到了這個(gè) peripheral溶锭,那你也不知道 central 和這個(gè) peripheral 當(dāng)時(shí)的具體狀態(tài)宝恶。但其實(shí)我們?cè)诨謴?fù)時(shí),是想恢復(fù)到程序被強(qiáng)制退出前的那一步趴捅。

這個(gè)需求垫毙,可以在代理方法centralManagerDidUpdateState:中,通過(guò)發(fā)現(xiàn)恢復(fù)的 peripheral 是否之前已經(jīng)成功連接來(lái)實(shí)現(xiàn):

上面的代碼描述了拱绑,當(dāng)系統(tǒng)在完成搜索 service 之后才退出的程序综芥,可以通過(guò)調(diào)用discoverServices:方法來(lái)恢復(fù) peripheral 的數(shù)據(jù)。如果 app 成功搜索到 service猎拨,你可以是否能搜索到需要的 characteristic(或者已經(jīng)訂閱過(guò))膀藐。通過(guò)更新初始化過(guò)程,可以確保在正確的時(shí)間點(diǎn)红省,調(diào)用正確的方法额各。

7、第三方框架

iOS 藍(lán)牙開(kāi)發(fā)中常用的第三方框架

BabyBluetooth

LGBluetooth

BeaconOSX

BluetoothSerial

作者:xiaofu666

鏈接:http://www.reibang.com/p/42b7c318ee24

來(lái)源:簡(jiǎn)書

著作權(quán)歸作者所有吧恃。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)虾啦,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

轉(zhuǎn)載聲明痕寓,僅為個(gè)人學(xué)習(xí)使用傲醉,翻閱原作者的上一篇,發(fā)現(xiàn)不能閱讀了呻率,所以就把本篇轉(zhuǎn)載一下硬毕。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市筷凤,隨后出現(xiàn)的幾起案子昭殉,更是在濱河造成了極大的恐慌,老刑警劉巖藐守,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挪丢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡卢厂,警方通過(guò)查閱死者的電腦和手機(jī)乾蓬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)慎恒,“玉大人任内,你說(shuō)我怎么就攤上這事撵渡。” “怎么了死嗦?”我有些...
    開(kāi)封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵趋距,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我越除,道長(zhǎng)节腐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任摘盆,我火速辦了婚禮翼雀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘孩擂。我一直安慰自己狼渊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布类垦。 她就那樣靜靜地躺著狈邑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪护锤。 梳的紋絲不亂的頭發(fā)上官地,一...
    開(kāi)封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天酿傍,我揣著相機(jī)與錄音烙懦,去河邊找鬼。 笑死赤炒,一個(gè)胖子當(dāng)著我的面吹牛氯析,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播莺褒,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼掩缓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了遵岩?” 一聲冷哼從身側(cè)響起你辣,我...
    開(kāi)封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尘执,沒(méi)想到半個(gè)月后舍哄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡誊锭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年表悬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丧靡。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蟆沫,死狀恐怖籽暇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饭庞,我是刑警寧澤戒悠,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站舟山,受9級(jí)特大地震影響救崔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捏顺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一六孵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幅骄,春花似錦劫窒、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至挪凑,卻和暖如春孕索,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背躏碳。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工搞旭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菇绵。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓肄渗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親咬最。 傳聞我的和親對(duì)象是個(gè)殘疾皇子翎嫡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容