Android低功耗藍(lán)牙使用介紹

最近在項(xiàng)目中需要實(shí)現(xiàn)一個(gè)藍(lán)牙搖一搖開門的功能,藍(lán)牙和門口機(jī)數(shù)據(jù)交互的協(xié)議已經(jīng)給定,考慮到藍(lán)牙的適配性,決定使用低功耗藍(lán)牙進(jìn)行開發(fā)。低功耗藍(lán)牙是Android 4.3開始支持的,其主要特點(diǎn)是主要特點(diǎn)是快速搜索,快速連接,超低功耗保持連接和數(shù)據(jù)傳輸,常被用在可穿戴設(shè)備中。

1. 開發(fā)之前的準(zhǔn)備工作

在進(jìn)行BLE(Bluetooth Low Energy)開發(fā)之前,我們先來(lái)了解一波BLE數(shù)據(jù)傳輸涉及到的關(guān)鍵概念:

Generic Attribute Profile (GATT)

通過(guò)BLE連接,讀寫屬性類小數(shù)據(jù)的Profile通用規(guī)范?,F(xiàn)在所有的BLE應(yīng)用Profile都是基于GATT的。

Attribute Protocol (ATT)

GATT是基于ATT Protocol的。ATT針對(duì)BLE設(shè)備做了專門的優(yōu)化,具體就是在傳輸過(guò)程中使用盡量少的數(shù)據(jù)。
每個(gè)屬性都有一個(gè)唯一的UUID,屬性將以characteristics and services的形式傳輸。

Characteristic

Characteristic可以理解為一個(gè)數(shù)據(jù)類型,它包括一個(gè)value和0至多個(gè)對(duì)次value的描述(Descriptor)。

Descriptor

對(duì)Characteristic的描述,例如范圍、計(jì)量單位等。

Service

Characteristic的集合。例如一個(gè)service叫做“Heart Rate Monitor”,
它可能包含多個(gè)Characteristics,其中可能包含一個(gè)叫做“heart rate measurement"的Characteristic。

2. 開發(fā)需要的權(quán)限

Android使用藍(lán)牙,需要聲明BLUETOOTH權(quán)限,如果需要掃描設(shè)備或者操作藍(lán)牙設(shè)置,則還需要BLUETOOTH_ADMIN權(quán)限,如果Android版本在5.0以上,藍(lán)牙還需要位置權(quán)限。

1.png

3. 藍(lán)牙的開啟和藍(lán)牙狀態(tài)的監(jiān)聽

在藍(lán)牙編程中,首先我們要知道如何去開啟和關(guān)閉藍(lán)牙,下面介紹兩種方法:

1) 通過(guò)獲取藍(lán)牙管理服務(wù)和適配器來(lái)開啟、關(guān)閉藍(lán)牙,獲取當(dāng)前藍(lán)牙是否為可用狀態(tài);

2.png

2) 跳轉(zhuǎn)到系統(tǒng)藍(lán)牙設(shè)置界面,由用戶自行開啟藍(lán)牙,并通過(guò)廣播監(jiān)聽藍(lán)牙開啟與關(guān)閉的通知,通過(guò)藍(lán)牙適配器來(lái)獲取當(dāng)前藍(lán)牙是否為可用狀態(tài):

跳轉(zhuǎn)藍(lán)牙設(shè)置界面

3.png

注冊(cè)藍(lán)牙狀態(tài)廣播監(jiān)聽

4.png

藍(lán)牙廣播監(jiān)聽,可以接受藍(lán)牙開啟,關(guān)閉時(shí)的通知:

5.png

藍(lán)牙是否開啟的判斷

6.png

第一種方法可以用來(lái)直接開關(guān)藍(lán)牙,如果是Android 6.0系統(tǒng)及以上的還需要添加動(dòng)態(tài)申請(qǐng)權(quán)限代碼;第二種方法是引導(dǎo)用戶進(jìn)入設(shè)置界面進(jìn)行手動(dòng)設(shè)置,再通過(guò)監(jiān)聽開關(guān)狀態(tài)來(lái)執(zhí)行后續(xù)操作,不需要?jiǎng)討B(tài)申請(qǐng)權(quán)限;

4. 藍(lán)牙設(shè)備的掃描和連接

1)啟動(dòng)藍(lán)牙后,我們就要開始使用藍(lán)牙進(jìn)行掃描,發(fā)現(xiàn)我們想要連接的設(shè)備。在Android5.0版本時(shí),Google對(duì)BLE更新了API,添加了新的掃描方法和回調(diào),在下面代碼中會(huì)進(jìn)行區(qū)分:

mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();

開始掃描

if(Build.VERSION.SDK_INT >= 21){
   mBluetoothLeScanner.startScan(mScanCallback);
}else{
  mBluetoothAdapter.startLeScan(mLeScanCallback);
}

關(guān)閉掃描

if(Build.VERSION.SDK_INT >= 21){
   mBluetoothLeScanner.stopScan(mScanCallback);
}else{
  mBluetoothAdapter.stopLeScan(mLeScanCallback);
}

掃描回調(diào)也不相同

7.png

2) 掃描找到想要連接的設(shè)備后,就可以開始進(jìn)行設(shè)備連接了:

8.png

注意:根據(jù)需要決定是否對(duì)有連接過(guò)的設(shè)備進(jìn)行重新連接。比如有些設(shè)備在連接通訊后就自行斷開,連接總時(shí)長(zhǎng)只有幾秒鐘的,這樣的不需要保留地址,每次都直接連接比較好。

連接設(shè)備時(shí),會(huì)觸發(fā)一個(gè)通信的回調(diào),這個(gè)回調(diào)比較重要,在設(shè)備連接,數(shù)據(jù)通信中都會(huì)用到:

9.png

在連接狀態(tài)回調(diào)中判斷連接狀態(tài),連接成功后,調(diào)用gatt.discoverServices()方法去發(fā)現(xiàn)連接的設(shè)備提供的服務(wù)及其特征和描述符;發(fā)現(xiàn)成功后,會(huì)調(diào)用onServicesDiscovered()回調(diào),表示可以與之通信了;

10.png

發(fā)現(xiàn)服務(wù)后,通過(guò)handler回調(diào)到主線程,執(zhí)行了下面的方法用來(lái)設(shè)置后續(xù)需要用到連接的服務(wù)節(jié)點(diǎn)下的Characteristic發(fā)生改變時(shí)進(jìn)行通知:

11.png

此處是獲取了開門服務(wù),設(shè)置開門服務(wù)下接受到命令返回的Characteristic

12.png

setCharacteristicNotification()方法會(huì)觸發(fā)onCharacteristicChanged()回調(diào)。接受到onCharacteristicChanged()回調(diào)后證明可以設(shè)置通知成功,可以開始發(fā)送通訊指令了。

13.png

5. 藍(lán)牙的數(shù)據(jù)發(fā)送和接收

經(jīng)過(guò)4中的步驟后,此時(shí)遠(yuǎn)程設(shè)備已經(jīng)準(zhǔn)備好進(jìn)行藍(lán)牙通訊了,下面開始發(fā)送指令:

14.png

此處是向開門服務(wù)下的接收命令的BluetoothGattCharacteristic對(duì)象寫入命令數(shù)據(jù)。

15.png

writeCharacteristic()方法會(huì)觸發(fā)onCharacteristicWrite()回調(diào)

16.png

注意:BLE向設(shè)備寫入數(shù)據(jù)時(shí),最大20byte,如果需要寫入的數(shù)據(jù)大于20byte,可以選擇將數(shù)據(jù)進(jìn)行分割,先發(fā)送前20byte,利用回調(diào)觸發(fā)將后續(xù)數(shù)據(jù)依次發(fā)送,每次最多20byte。

遠(yuǎn)程設(shè)備接收到完整指令并執(zhí)行后,特征值變化會(huì)觸發(fā)onCharacteristicChanged()回調(diào),并傳回?cái)?shù)據(jù)

17.png

這樣,一個(gè)完整的通信循環(huán)就完成了。

6. 總結(jié)

藍(lán)牙BLE本身的流程并不復(fù)雜,關(guān)鍵在于存在有許多特殊情況需要處理,下面對(duì)特殊情況的處理進(jìn)行敘述和總結(jié):

1、 調(diào)用gatt.discoverServices()方法后不執(zhí)行onServiceDiscovered()回調(diào),這種問題有兩種情況,一種是設(shè)備距離遠(yuǎn),回調(diào)時(shí)間過(guò)長(zhǎng),一種是并沒有真正連接到設(shè)備,可以嘗試將gatt.discoverServices()方法放到主線程中執(zhí)行;

2、 onServicesDiscovered 回調(diào)里不能直接執(zhí)行 write /readDataFromCharacteristic() 或者 enableNotificationOfCharacteristic之類的,而要放到主線程里執(zhí)行,可以使用 handler轉(zhuǎn)換線程;

3、 掃描盡量不要放在主線程進(jìn)行,可以放入子線程中,部分機(jī)型會(huì)出現(xiàn)do too many work in main thread。

4、 任何出錯(cuò),超時(shí),用完就馬上調(diào)用Gatt.disconnect(), Gatt.close()。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容