Android BLE 藍(lán)牙開(kāi)發(fā),連接藍(lán)牙設(shè)備進(jìn)行通訊
1. 介紹
本篇主要基于 Android 官方的低功耗藍(lán)牙連接服務(wù)。
講解如何通過(guò) UUID 連接藍(lán)牙設(shè)備。如果你針對(duì) GATT 服務(wù)不太了解。那么這篇應(yīng)該能夠稍微幫助到你。
官方文檔地址:https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le?hl=zh_cn#connect
2. 概念
如果是老用戶(hù)了,那么就應(yīng)該知道曾經(jīng)藍(lán)牙設(shè)備是一個(gè)高耗電的部件。根本不可能長(zhǎng)時(shí)間開(kāi)啟。而在藍(lán)牙4.0版本之后,藍(lán)牙的通訊,耗電,抗干擾都得到了顯著提升。同時(shí)藍(lán)牙成本也得到了降低。
然后才有了我們現(xiàn)在的各種穿戴設(shè)備例如手環(huán),藍(lán)牙耳機(jī),藍(lán)牙電子秤,藍(lán)牙音箱等等的爆發(fā)。
同時(shí),其他工業(yè)或者外置設(shè)備也都開(kāi)始大量支持藍(lán)牙通訊。因?yàn)槟芎暮统杀窘档土恕?/p>
針對(duì)低功耗藍(lán)牙通訊,Android 4.3(API 18)開(kāi)始引入了 BLE 庫(kù)。我們可以直接使用 Android SDK 中的藍(lán)牙 BLE 庫(kù),而不用額外導(dǎo)入依賴(lài)庫(kù)。
以前開(kāi)發(fā)藍(lán)牙通訊,還需要實(shí)現(xiàn)藍(lán)牙配對(duì)。需要主動(dòng)跳轉(zhuǎn)到手機(jī)設(shè)置界面進(jìn)行PIN碼配對(duì),然后配對(duì)通過(guò)之后才能進(jìn)行藍(lán)牙鏈接。
而使用BLE庫(kù),我們可以直接通過(guò)藍(lán)牙設(shè)備的UUID進(jìn)行連接(通過(guò)GATT服務(wù)),在當(dāng)前應(yīng)用內(nèi)就能直接連接了。而不用通過(guò)系統(tǒng)設(shè)置。
市面上的各種手環(huán)的自動(dòng)匹配鏈接,電子秤的自動(dòng)連接等等都是通過(guò)GATT進(jìn)行通訊和鏈接的。
2.1 術(shù)語(yǔ)
- GATT:全稱(chēng)為:Generic Attribute Profile,翻譯為:通用屬性配置文件。GATT 配置文件是一種通用規(guī)范,內(nèi)容針對(duì)在 BLE 鏈路上發(fā)送和接收稱(chēng)為“屬性ATT”的簡(jiǎn)短數(shù)據(jù)片段。目前所有低功耗應(yīng)用配置文件均以 GATT 為基礎(chǔ)。
- ATT:全稱(chēng)為:Attribute Protocol,翻譯為:屬性協(xié)議。它是 GATT 的構(gòu)建基礎(chǔ),二者的關(guān)系也被稱(chēng)為 GATT/ATT。每個(gè)屬性均由通用唯一標(biāo)識(shí)符 (UUID) 進(jìn)行唯一標(biāo)識(shí),后者是用于對(duì)信息進(jìn)行唯一標(biāo)識(shí)的字符串 ID 的 128 位標(biāo)準(zhǔn)化格式。由 ATT 傳輸?shù)膶傩圆捎锰卣骱头?wù)格式。
- 特征 Characteristic: 特征包含一個(gè)值和 0 至多個(gè)描述特征值的描述符。您可將特征理解為類(lèi)型,后者與類(lèi)類(lèi)似。
- 描述符:描述符是描述特征值的已定義屬性。例如,描述符可指定人類(lèi)可讀的描述、特征值的可接受范圍或特定于特征值的度量單位。
- Service — 服務(wù)是一系列特征。例如,您可能擁有名為“心率監(jiān)測(cè)器”的服務(wù),其中包括“心率測(cè)量”等特征。
以上術(shù)語(yǔ)的介紹來(lái)源于Android官網(wǎng)
2.2 通訊過(guò)程
假如我們有一個(gè)藍(lán)牙外置設(shè)備(Device),然后有一個(gè)支持藍(lán)牙的移動(dòng)設(shè)備(Phone)。兩者之間的通訊方式步驟是:
- Device 開(kāi)啟藍(lán)牙。(通常這些設(shè)備都是開(kāi)機(jī)之后,就默認(rèn)開(kāi)啟藍(lán)牙了)
- Phone 開(kāi)啟藍(lán)牙。
- Phone 發(fā)現(xiàn) Device。
- Phone 與 Device 創(chuàng)建藍(lán)牙連接。
- Phone 創(chuàng)建 Gatt 客戶(hù)端,與 Device Gatt 服務(wù)端連接。
- Phone 通過(guò) Gatt 服務(wù)功能獲取 Device 中的消息,并發(fā)送消息給 Device 設(shè)備。
整個(gè)過(guò)程就是這樣的。下面我也將按照這個(gè)通訊過(guò)程進(jìn)行介紹。
3.開(kāi)發(fā)
基于我的使用情況,從無(wú)到有的介紹,完整的藍(lán)牙開(kāi)發(fā)配置過(guò)程。給大家一個(gè)參考
語(yǔ)言主要為 Java
3.1 權(quán)限
要在應(yīng)用中使用藍(lán)牙功能,必須聲明 BLUETOOTH 藍(lán)牙權(quán)限。需要此權(quán)限才能執(zhí)行任何藍(lán)牙通信,例如請(qǐng)求連接、接受連接和傳輸數(shù)據(jù)等。
同時(shí),還需要位置權(quán)限。因?yàn)樗{(lán)牙 LE 信標(biāo)通常與位置相關(guān)聯(lián)。如果不開(kāi)啟 ACCESS_FINE_LOCATION 權(quán)限。那么我們將會(huì)無(wú)法發(fā)現(xiàn)藍(lán)牙設(shè)備。
也就是執(zhí)行藍(lán)牙掃描 API 無(wú)法得到任何結(jié)果(PS::Logcat 中的錯(cuò)誤日志會(huì)告訴你,要開(kāi)啟位置權(quán)限,否則無(wú)法掃描發(fā)現(xiàn)藍(lán)牙設(shè)備)。
其中 android.permission.ACCESS_FINE_LOCATION? 是高版本API 28 權(quán)限。如果要支持更低版本,就需要申請(qǐng)<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />。
如果要執(zhí)行藍(lán)牙掃描功能,我們需要申請(qǐng):<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />權(quán)限
如果要執(zhí)行藍(lán)牙鏈接,開(kāi)關(guān)藍(lán)牙。需要申請(qǐng):<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />權(quán)限
而上面兩個(gè)權(quán)限呢,是在 API 31 上才有效。而低版本就是申請(qǐng):
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />權(quán)限也就夠了。
權(quán)限配置完畢之后,就是代碼開(kāi)發(fā)了。
不管是高版本,還是低版本。將權(quán)限都申請(qǐng)可以說(shuō)最穩(wěn)妥了。
3.2 檢測(cè)設(shè)備是否支持藍(lán)牙
通常情況下,手機(jī)是有藍(lán)牙的。而我們?nèi)绻谄渌?Android 系統(tǒng)的設(shè)備中,例如TV,平板,一體機(jī)等等。是否有藍(lán)牙還真不能完整保證。
如果不確定的情況下,那么可以通過(guò)以下代碼檢查 BLE 的可用性。
藍(lán)牙是否開(kāi)啟都不影響檢查結(jié)果。它檢查的是設(shè)備是否有藍(lán)牙功能,而不是藍(lán)牙是否啟動(dòng),下面會(huì)介紹如何判斷藍(lán)牙是否啟動(dòng)
3.3 開(kāi)啟藍(lán)牙
當(dāng)我們?cè)O(shè)備也支持藍(lán)牙了,權(quán)限也配置了。下一步就是獲取 BluetoothAdapter 對(duì)象了。
我們后續(xù)控制藍(lán)牙的狀態(tài),都是通過(guò)該方法實(shí)現(xiàn)的。
首先,檢測(cè)藍(lán)牙是否開(kāi)啟??梢酝ㄟ^(guò)isEnabled()方法進(jìn)行檢測(cè):
我們其實(shí)可以直接使用bluetoothAdapter.enable()開(kāi)啟藍(lán)牙。當(dāng)藍(lán)牙沒(méi)有開(kāi)啟時(shí),我們可以直接開(kāi)啟藍(lán)牙。
這個(gè)方法的結(jié)果,并不是實(shí)時(shí)返回的。我們?nèi)绻浪{(lán)牙是否開(kāi)啟,需要監(jiān)聽(tīng)藍(lán)牙狀態(tài)的廣播才行。下面會(huì)介紹廣播監(jiān)聽(tīng)。
PS:這個(gè)方法需要android.Manifest.permission.BLUETOOTH_CONNECT 權(quán)限才能使用。
官方是建議我們通過(guò)Intent讓系統(tǒng)設(shè)置進(jìn)行開(kāi)啟藍(lán)牙的。
但是現(xiàn)在startActivityForResult方法已經(jīng)過(guò)時(shí)。我們可以使用Launcher來(lái)調(diào)用:
上面的 launcher?需要在Activity? 的 onCreate 方法中初始化。然后在需要進(jìn)行藍(lán)牙設(shè)置界面啟動(dòng)的地方配置:
我們?nèi)绻褂胋luetoothAdapter.enable();?時(shí)Android Studio出現(xiàn)代碼錯(cuò)誤警告,可以在該代碼使用的方法中添加:@SuppressLint("MissingPermission")注解。
3.4 廣播監(jiān)聽(tīng)
其實(shí)這個(gè)廣播監(jiān)聽(tīng),是否需要。根據(jù)大家實(shí)際情況來(lái)定。不一定需要。
首先,創(chuàng)建一個(gè)動(dòng)態(tài)廣播對(duì)象:
然后進(jìn)行廣播注冊(cè):
注冊(cè)完畢后,在onDestroy方法中需要注銷(xiāo)注冊(cè):
其實(shí),我們只需要藍(lán)牙狀態(tài)的監(jiān)聽(tīng)就可以了BluetoothAdapter.ACTION_STATE_CHANGED 其他的設(shè)備查找,配對(duì)。可以不用,因?yàn)橛|發(fā)到廣播的設(shè)備查找效率太低,而且多次重復(fù)查找時(shí),還會(huì)出現(xiàn)耗時(shí)變長(zhǎng)。設(shè)備無(wú)法查找到的情況。
3.5 藍(lán)牙設(shè)備查找
官方文檔上推薦的查找方式是:
可是現(xiàn)在這個(gè)方法也過(guò)時(shí)了。替換方法是:
onScanResult方法是一個(gè)在子線程觸發(fā)的回調(diào),我們不能在該方法中直接操作UI對(duì)象。
其次,掃描到一個(gè)藍(lán)牙設(shè)備就會(huì)觸發(fā)一次消息回調(diào)。我們可以得到一個(gè)BluetoothDevice對(duì)象。 也就是說(shuō)這個(gè)方法中會(huì)觸發(fā)多次回調(diào),
所以建議,在掃描到我們的藍(lán)牙設(shè)備之后,主動(dòng)調(diào)用scanner.stopScan(callback);停止掃描。
PS:這種查找方式,不會(huì)觸發(fā)藍(lán)牙的遍歷廣播。我們?nèi)绻_(kāi)啟廣播進(jìn)行監(jiān)聽(tīng)設(shè)備掃描情況。如果通過(guò)startScan方法,廣播中不會(huì)有回調(diào)。
上面是一個(gè)通用搜索模式,我們還可以配置自己的過(guò)濾條件。例如:
其中ScanFilter?對(duì)象,我們可以配置我們想查找的藍(lán)牙設(shè)備的信息??梢允莝etDeviceName,setServiceUuid,setDeviceAddress,setServiceSolicitationUuid等。
ScanSettings對(duì)象是可以定義我們的掃描模式,通過(guò)配置該項(xiàng)可以提高掃描效率。
默認(rèn)情況下,執(zhí)行的是:SCAN_MODE_LOW_POWER在低功耗模式下執(zhí)行藍(lán)牙LE掃描。 這是默認(rèn)的掃描模式,因?yàn)樗淖钌俚碾娏俊?/p>
3.5.1 startDiscovery
如果上面的方法還不滿足我們的情況,可以使用:
我們可以直接使用bluetoothAdapter進(jìn)行掃描。這個(gè)方法觸發(fā)之后是由系統(tǒng)進(jìn)行藍(lán)牙掃描。就和我們?cè)谑謾C(jī)的設(shè)置界面中點(diǎn)擊藍(lán)牙掃描一樣。
上面的這個(gè)方法沒(méi)有回調(diào),因?yàn)樗械乃{(lán)牙設(shè)備的發(fā)現(xiàn)都將通過(guò)廣播事件進(jìn)行傳遞。
需要通過(guò)我上面的廣播監(jiān)聽(tīng)介紹的內(nèi)容。進(jìn)行實(shí)時(shí)獲取到掃描到的設(shè)備。
使用上面的方法有幾個(gè)缺點(diǎn):
1.效率慢,耗時(shí)很長(zhǎng)。
2.重復(fù)掃描會(huì)失敗。不能說(shuō)是失敗了,而是系統(tǒng)會(huì)將重復(fù)掃描的請(qǐng)求進(jìn)行阻止,關(guān)鍵的問(wèn)題在于這個(gè)阻止操作是手機(jī)廠商定制的。
PS:不管是BluetoothLeScanner? 還是bluetoothAdapter.startDiscovery() 去查找藍(lán)牙設(shè)備。都不建議一直重復(fù)掃描。否則會(huì)出現(xiàn)無(wú)法掃描到設(shè)備,沒(méi)有任何掃描結(jié)果等等情況。因?yàn)閽呙枋且粋€(gè)耗時(shí)耗電的操作。
3.6 鏈接Gatt
當(dāng)我們掃描到了藍(lán)牙設(shè)備之后,就會(huì)獲取到BluetoothDevice?對(duì)象。然后我們通過(guò)BluetoothDevice?對(duì)象創(chuàng)建GATT服務(wù)進(jìn)行后續(xù)的藍(lán)牙通訊。
第一個(gè)傳參context沒(méi)有什么可以介紹的。
第二個(gè)傳參autoConnect:是一個(gè)boolean值對(duì)象,false代表直接連接到藍(lán)牙設(shè)備。true代表在藍(lán)牙設(shè)備可用時(shí)自動(dòng)連接。
第三個(gè)參數(shù)BluetoothGattCallback 是Gatt服務(wù)的各種回調(diào)了。
我們通過(guò)gattCallback回調(diào)的內(nèi)容,來(lái)得到與藍(lán)牙設(shè)備的鏈接狀態(tài),數(shù)據(jù)通信內(nèi)容等。
下面來(lái)詳細(xì)介紹下BluetoothGattCallback對(duì)象的幾個(gè)方法。
我們可以通過(guò)鏈接成功和鏈接斷開(kāi)。來(lái)判斷我們當(dāng)前與藍(lán)牙設(shè)備的通訊狀態(tài)。
當(dāng)我們比對(duì)Service?的UUID成功之后, 我們就可以獲取Service的Characteristic對(duì)象。該對(duì)象也就是特征。通過(guò)注冊(cè)特征來(lái)實(shí)現(xiàn)消息的監(jiān)聽(tīng)和發(fā)送業(yè)務(wù)。
3.7 注冊(cè)消息監(jiān)聽(tīng)-setCharacteristicNotification
在上面的示例中:READ_DEDSCRIPTION_UUID = "00002902-0000-1000-8000-00805f9b34fb" 是固定的,不管你鏈接什么樣的藍(lán)牙設(shè)備。
在注冊(cè)消息監(jiān)聽(tīng),都是使用UUID值是00002902-0000-1000-8000-00805f9b34fb進(jìn)行的。這個(gè)是Android系統(tǒng)保留的。用于動(dòng)態(tài)監(jiān)聽(tīng)的。
你如果不想使用這個(gè)動(dòng)態(tài)監(jiān)聽(tīng)。就需要自己寫(xiě)線程主動(dòng)去輪詢(xún)獲取到藍(lán)牙設(shè)備發(fā)送過(guò)來(lái)的消息了。
到這里,我們其實(shí)就能夠?qū)崿F(xiàn)藍(lán)牙設(shè)備的實(shí)時(shí)監(jiān)聽(tīng),并得到消息內(nèi)容了。
3.8 寫(xiě)數(shù)據(jù)到藍(lán)牙設(shè)備中
我們?nèi)绻雽?nèi)容推送到藍(lán)牙設(shè)備中,在發(fā)現(xiàn)服務(wù)的時(shí)候onServicesDiscovered 遍歷特性中,確保是用于寫(xiě)消息的特性對(duì)象后。選擇持有該特性,然后通過(guò):
3.9 關(guān)閉連接
當(dāng)藍(lán)牙通訊結(jié)束,或者界面關(guān)閉時(shí)。我們需要關(guān)閉GATT服務(wù),減少資源占用。
也可以關(guān)閉BluetoothGattCallback 的回調(diào)監(jiān)聽(tīng):
4. 小結(jié)
到這里藍(lán)牙的鏈接和讀取就結(jié)束了。
我們通過(guò)bluetoothAdapter 查找到藍(lán)牙設(shè)備之后,再通過(guò)GATT服務(wù)進(jìn)行藍(lán)牙設(shè)備與手機(jī)之間的配對(duì)。直接比對(duì)UUID,而不再需要PIN碼進(jìn)行配對(duì)了。
(PS:有些安全性要求比較高的設(shè)備,還是會(huì)需要主動(dòng)進(jìn)行PIN碼配對(duì)。PIN配隊(duì)就只能通過(guò)系統(tǒng)設(shè)備界面中的藍(lán)牙功能項(xiàng)進(jìn)行操作了。)
通過(guò)GATT服務(wù)連接成功后。就可以查詢(xún)?cè)揝erver下的各種特性了,不同的特性對(duì)應(yīng)了一個(gè)功能。有發(fā)消息的特性,也有用于收消息的特性。
同時(shí)一個(gè)藍(lán)牙設(shè)備對(duì)象,可能有多種服務(wù)功能。
如果不想自己寫(xiě)線程變量輪詢(xún)?cè)O(shè)備發(fā)送過(guò)來(lái)的消息,就通過(guò)注冊(cè)消息監(jiān)聽(tīng)。讓BLE框架幫我們進(jìn)行輪詢(xún)之后,再通知到我們。



























