1. 介紹
本篇主要基于 Android 官方的低功耗藍牙連接服務(wù)。
講解如何通過 UUID 連接藍牙設(shè)備。如果你針對 GATT 服務(wù)不太了解。那么這篇應(yīng)該能夠稍微幫助到你。
官方文檔地址:https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le?hl=zh_cn#connect
2. 概念
如果是老用戶了,那么就應(yīng)該知道曾經(jīng)藍牙設(shè)備是一個高耗電的部件。根本不可能長時間開啟。而在藍牙4.0版本之后,藍牙的通訊,耗電,抗干擾都得到了顯著提升。同時藍牙成本也得到了降低。
然后才有了我們現(xiàn)在的各種穿戴設(shè)備例如手環(huán),藍牙耳機,藍牙電子秤,藍牙音箱等等的爆發(fā)。
同時,其他工業(yè)或者外置設(shè)備也都開始大量支持藍牙通訊。因為能耗和成本降低了。
針對低功耗藍牙通訊,Android 4.3(API 18)開始引入了 BLE 庫。我們可以直接使用 Android SDK 中的藍牙 BLE 庫,而不用額外導(dǎo)入依賴庫。
以前開發(fā)藍牙通訊,還需要實現(xiàn)藍牙配對。需要主動跳轉(zhuǎn)到手機設(shè)置界面進行PIN碼配對,然后配對通過之后才能進行藍牙鏈接。
而使用BLE庫,我們可以直接通過藍牙設(shè)備的UUID進行連接(通過GATT服務(wù)),在當前應(yīng)用內(nèi)就能直接連接了。而不用通過系統(tǒng)設(shè)置。
市面上的各種手環(huán)的自動匹配鏈接,電子秤的自動連接等等都是通過GATT進行通訊和鏈接的。
2.1 術(shù)語
- GATT:全稱為:Generic Attribute Profile,翻譯為:通用屬性配置文件。GATT 配置文件是一種通用規(guī)范,內(nèi)容針對在 BLE 鏈路上發(fā)送和接收稱為“屬性ATT”的簡短數(shù)據(jù)片段。目前所有低功耗應(yīng)用配置文件均以 GATT 為基礎(chǔ)。
- ATT:全稱為:Attribute Protocol,翻譯為:屬性協(xié)議。它是 GATT 的構(gòu)建基礎(chǔ),二者的關(guān)系也被稱為 GATT/ATT。每個屬性均由通用唯一標識符 (UUID) 進行唯一標識,后者是用于對信息進行唯一標識的字符串 ID 的 128 位標準化格式。由 ATT 傳輸?shù)膶傩圆捎锰卣骱头?wù)格式。
- 特征 Characteristic: 特征包含一個值和 0 至多個描述特征值的描述符。您可將特征理解為類型,后者與類類似。
- 描述符:描述符是描述特征值的已定義屬性。例如,描述符可指定人類可讀的描述、特征值的可接受范圍或特定于特征值的度量單位。
- Service — 服務(wù)是一系列特征。例如,您可能擁有名為“心率監(jiān)測器”的服務(wù),其中包括“心率測量”等特征。
以上術(shù)語的介紹來源于Android官網(wǎng)
2.2 通訊過程
假如我們有一個藍牙外置設(shè)備(Device),然后有一個支持藍牙的移動設(shè)備(Phone)。兩者之間的通訊方式步驟是:
- Device 開啟藍牙。(通常這些設(shè)備都是開機之后,就默認開啟藍牙了)
- Phone 開啟藍牙。
- Phone 發(fā)現(xiàn) Device。
- Phone 與 Device 創(chuàng)建藍牙連接。
- Phone 創(chuàng)建 Gatt 客戶端,與 Device Gatt 服務(wù)端連接。
- Phone 通過 Gatt 服務(wù)功能獲取 Device 中的消息,并發(fā)送消息給 Device 設(shè)備。
整個過程就是這樣的。下面我也將按照這個通訊過程進行介紹。
3.開發(fā)
基于我的使用情況,從無到有的介紹,完整的藍牙開發(fā)配置過程。給大家一個參考
語言主要為 Java
3.1 權(quán)限
要在應(yīng)用中使用藍牙功能,必須聲明 BLUETOOTH 藍牙權(quán)限。需要此權(quán)限才能執(zhí)行任何藍牙通信,例如請求連接、接受連接和傳輸數(shù)據(jù)等。
同時,還需要位置權(quán)限。因為藍牙 LE 信標通常與位置相關(guān)聯(lián)。如果不開啟 ACCESS_FINE_LOCATION 權(quán)限。那么我們將會無法發(fā)現(xiàn)藍牙設(shè)備。
也就是執(zhí)行藍牙掃描 API 無法得到任何結(jié)果(PS::Logcat 中的錯誤日志會告訴你,要開啟位置權(quán)限,否則無法掃描發(fā)現(xiàn)藍牙設(shè)備)。
<!-- 藍牙搜索配對 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- 操縱藍牙的開啟-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 如果應(yīng)用必須安裝在支持藍牙的設(shè)備上,可以將下面的required的值設(shè)置為true。-->
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />
其中 android.permission.ACCESS_FINE_LOCATION? 是高版本API 28 權(quán)限。如果要支持更低版本,就需要申請<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />。
如果要執(zhí)行藍牙掃描功能,我們需要申請:<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />權(quán)限
如果要執(zhí)行藍牙鏈接,開關(guān)藍牙。需要申請:<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />權(quán)限
而上面兩個權(quán)限呢,是在 API 31 上才有效。而低版本就是申請:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />權(quán)限也就夠了。
權(quán)限配置完畢之后,就是代碼開發(fā)了。
不管是高版本,還是低版本。將權(quán)限都申請可以說最穩(wěn)妥了。
3.2 檢測設(shè)備是否支持藍牙
通常情況下,手機是有藍牙的。而我們?nèi)绻谄渌?Android 系統(tǒng)的設(shè)備中,例如TV,平板,一體機等等。是否有藍牙還真不能完整保證。
如果不確定的情況下,那么可以通過以下代碼檢查 BLE 的可用性。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
//不支持藍牙設(shè)備
finish();
} else {
//支持藍牙設(shè)備
}
藍牙是否開啟都不影響檢查結(jié)果。它檢查的是設(shè)備是否有藍牙功能,而不是藍牙是否啟動,下面會介紹如何判斷藍牙是否啟動
3.3 開啟藍牙
當我們設(shè)備也支持藍牙了,權(quán)限也配置了。下一步就是獲取 BluetoothAdapter 對象了。
final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
我們后續(xù)控制藍牙的狀態(tài),都是通過該方法實現(xiàn)的。
首先,檢測藍牙是否開啟。可以通過isEnabled()方法進行檢測:
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
//開啟設(shè)備的藍牙鏈接
bluetoothAdapter.enable();//開啟藍牙
//動態(tài)判斷是否擁有位置權(quán)限ACCESS_COARSE_LOCATION 或ACCESS_FINE_LOCATION ,然后再執(zhí)行藍牙掃描
} else {
//動態(tài)判斷是否擁有位置權(quán)限ACCESS_COARSE_LOCATION 或ACCESS_FINE_LOCATION,然后再執(zhí)行藍牙掃描
}
我們其實可以直接使用bluetoothAdapter.enable()開啟藍牙。當藍牙沒有開啟時,我們可以直接開啟藍牙。
這個方法的結(jié)果,并不是實時返回的。我們?nèi)绻浪{牙是否開啟,需要監(jiān)聽藍牙狀態(tài)的廣播才行。下面會介紹廣播監(jiān)聽。
PS:這個方法需要android.Manifest.permission.BLUETOOTH_CONNECT 權(quán)限才能使用。
官方是建議我們通過Intent讓系統(tǒng)設(shè)置進行開啟藍牙的。
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
但是現(xiàn)在startActivityForResult方法已經(jīng)過時。我們可以使用Launcher來調(diào)用:
ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK) {
//處理返回結(jié)果
}
});
上面的 launcher?需要在Activity? 的 onCreate 方法中初始化。然后在需要進行藍牙設(shè)置界面啟動的地方配置:
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //創(chuàng)建一個藍牙啟動的意圖
launcher.launch(enableBtIntent);//使用launcer啟動這個意圖就可以了。
我們?nèi)绻褂胋luetoothAdapter.enable();?時Android Studio出現(xiàn)代碼錯誤警告,可以在該代碼使用的方法中添加:@SuppressLint("MissingPermission")注解。
3.4 廣播監(jiān)聽
其實這個廣播監(jiān)聽,是否需要。根據(jù)大家實際情況來定。不一定需要。
首先,創(chuàng)建一個動態(tài)廣播對象:
public class BluetoothFoundReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//監(jiān)聽藍牙狀態(tài)之后,發(fā)送消息
try {
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
//開始掃描
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
//結(jié)束掃描
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
//發(fā)現(xiàn)設(shè)備,每掃碼到一個設(shè)備,都會觸發(fā)一次
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//我們可以得到藍牙設(shè)備
} else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
//藍牙開關(guān)狀態(tài)
int statue = bluetoothAdapter.getState();
switch (statue) {
case BluetoothAdapter.STATE_OFF:
Log.e(TAG, "藍牙狀態(tài):,藍牙關(guān)閉");
break;
case BluetoothAdapter.STATE_ON:
Log.e(TAG, "藍牙狀態(tài):,藍牙打開");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.e(TAG, "藍牙狀態(tài):,藍牙正在關(guān)閉");
break;
case BluetoothAdapter.STATE_TURNING_ON:
Log.e(TAG, "藍牙狀態(tài):,藍牙正在打開");
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后進行廣播注冊:
bluetoothFoundReceiver = new BluetoothFoundReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//連接藍牙,斷開藍牙
filter.addAction(BluetoothDevice.ACTION_FOUND);//找到設(shè)備的廣播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索完成的廣播
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//狀態(tài)改變 配對開始時,配對成功時
registerReceiver(bluetoothFoundReceiver, filter);
注冊完畢后,在onDestroy方法中需要注銷注冊:
@Override
protected void onDestroy() {
if (bluetoothFoundReceiver != null)
unregisterReceiver(bluetoothFoundReceiver); //停止監(jiān)聽
super.onDestroy();
}
其實,我們只需要藍牙狀態(tài)的監(jiān)聽就可以了BluetoothAdapter.ACTION_STATE_CHANGED 其他的設(shè)備查找,配對。可以不用,因為觸發(fā)到廣播的設(shè)備查找效率太低,而且多次重復(fù)查找時,還會出現(xiàn)耗時變長。設(shè)備無法查找到的情況。
3.5 藍牙設(shè)備查找
官方文檔上推薦的查找方式是:
bluetoothAdapter.startLeScan(leScanCallback); //查找
bluetoothAdapter.stopLeScan(leScanCallback); //停止查找
可是現(xiàn)在這個方法也過時了。替換方法是:
BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
//不進行權(quán)限驗證
ScanCallback callback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice device = result.getDevice();//得到設(shè)備
// Log.e(TAG, "發(fā)現(xiàn)設(shè)備" + device.getName());
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e(TAG, "搜索錯誤" + errorCode);
}
};
scanner.startScan(callback);
onScanResult方法是一個在子線程觸發(fā)的回調(diào),我們不能在該方法中直接操作UI對象。
其次,掃描到一個藍牙設(shè)備就會觸發(fā)一次消息回調(diào)。我們可以得到一個BluetoothDevice對象。 也就是說這個方法中會觸發(fā)多次回調(diào),
所以建議,在掃描到我們的藍牙設(shè)備之后,主動調(diào)用scanner.stopScan(callback);停止掃描。
PS:這種查找方式,不會觸發(fā)藍牙的遍歷廣播。我們?nèi)绻_啟廣播進行監(jiān)聽設(shè)備掃描情況。如果通過startScan方法,廣播中不會有回調(diào)。
上面是一個通用搜索模式,我們還可以配置自己的過濾條件。例如:
ScanFilter sn =new ScanFilter.Builder().setDeviceName("藍牙設(shè)備的名稱").setServiceUuid(ParcelUuid.fromString("我們的設(shè)備的Service UUID")).build();
List<ScanFilter> scanFilters=new ArrayList<>();
scanFilters.add(sn);
scanner.startScan(scanFilters, new ScanSettings.Builder().build(),callback);
其中ScanFilter?對象,我們可以配置我們想查找的藍牙設(shè)備的信息??梢允莝etDeviceName,setServiceUuid,setDeviceAddress,setServiceSolicitationUuid等。
ScanSettings對象是可以定義我們的掃描模式,通過配置該項可以提高掃描效率。
默認情況下,執(zhí)行的是:SCAN_MODE_LOW_POWER在低功耗模式下執(zhí)行藍牙LE掃描。 這是默認的掃描模式,因為它消耗最少的電量。
3.5.1 startDiscovery
如果上面的方法還不滿足我們的情況,可以使用:
if (bluetoothAdapter.isDiscovering()) {//是否在掃描
bluetoothAdapter.cancelDiscovery(); //停止掃描
}
//查找藍牙
bluetoothAdapter.startDiscovery();
我們可以直接使用bluetoothAdapter進行掃描。這個方法觸發(fā)之后是由系統(tǒng)進行藍牙掃描。就和我們在手機的設(shè)置界面中點擊藍牙掃描一樣。
上面的這個方法沒有回調(diào),因為所有的藍牙設(shè)備的發(fā)現(xiàn)都將通過廣播事件進行傳遞。
需要通過我上面的廣播監(jiān)聽介紹的內(nèi)容。進行實時獲取到掃描到的設(shè)備。
使用上面的方法有幾個缺點:
1.效率慢,耗時很長。
2.重復(fù)掃描會失敗。不能說是失敗了,而是系統(tǒng)會將重復(fù)掃描的請求進行阻止,關(guān)鍵的問題在于這個阻止操作是手機廠商定制的。
PS:不管是BluetoothLeScanner? 還是bluetoothAdapter.startDiscovery() 去查找藍牙設(shè)備。都不建議一直重復(fù)掃描。否則會出現(xiàn)無法掃描到設(shè)備,沒有任何掃描結(jié)果等等情況。因為掃描是一個耗時耗電的操作。
3.6 鏈接Gatt
當我們掃描到了藍牙設(shè)備之后,就會獲取到BluetoothDevice?對象。然后我們通過BluetoothDevice?對象創(chuàng)建GATT服務(wù)進行后續(xù)的藍牙通訊。
BluetoothDevice device;// 當我們通過掃描得到device對象之后,進行Gatt服務(wù)創(chuàng)建
BluetoothGatt bluetoothGatt = device.connectGatt(this, false, gattCallback);
第一個傳參context沒有什么可以介紹的。
第二個傳參autoConnect:是一個boolean值對象,false代表直接連接到藍牙設(shè)備。true代表在藍牙設(shè)備可用時自動連接。
第三個參數(shù)BluetoothGattCallback 是Gatt服務(wù)的各種回調(diào)了。
我們通過gattCallback回調(diào)的內(nèi)容,來得到與藍牙設(shè)備的鏈接狀態(tài),數(shù)據(jù)通信內(nèi)容等。
下面來詳細介紹下BluetoothGattCallback對象的幾個方法。
String SERVICE_UUID="00000-000000-000000-000000";//這個是我要鏈接的藍牙設(shè)備的ServiceUUID
BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
//GATT的鏈接狀態(tài)回調(diào)
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
Log.v(TAG, "連接成功");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.e(TAG, "連接斷開");
} else if (newState == BluetoothProfile.STATE_CONNECTING) {
//TODO 在實際過程中,該方法并沒有調(diào)用
Log.e(TAG, "連接中....");
}
}
//獲取GATT服務(wù)發(fā)現(xiàn)后的回調(diào)
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.e(TAG, "GATT_SUCCESS"); //服務(wù)發(fā)現(xiàn)
for (BluetoothGattService bluetoothGattService : gatt.getServices()) {
Log.e(TAG, "Service_UUID" + bluetoothGattService.getUuid()); // 我們可以遍歷到該藍牙設(shè)備的全部Service對象。然后通過比較Service的UUID,我們可以區(qū)分該服務(wù)是屬于什么業(yè)務(wù)的
if (SERVICE_UUID.equals(bluetoothGattService.getUuid().toString())) {
for (BluetoothGattCharacteristic characteristic : bluetoothGattService.getCharacteristics()) {
prepareBroadcastDataNotify(gatt, characteristic); //給滿足條件的屬性配置上消息通知
}
return;//結(jié)束循環(huán)操作
}
}
} else {
Log.e(TAG, "onServicesDiscovered received: " + status);
}
}
//藍牙設(shè)備發(fā)送消息后的自動監(jiān)聽
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// readUUID 是我要鏈接的藍牙設(shè)備的消息讀UUID值,跟通知的特性的UUID比較。這樣可以避免其他消息的污染。
if (READ_UUID.equals(characteristic.getUuid().toString())) {
try {
String chara = new String(characteristic.getValue(), "UTF-8");
Log.e(TAG, "消息內(nèi)容:" + chara);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
};
我們可以通過鏈接成功和鏈接斷開。來判斷我們當前與藍牙設(shè)備的通訊狀態(tài)。
當我們比對Service?的UUID成功之后, 我們就可以獲取Service的Characteristic對象。該對象也就是特征。通過注冊特征來實現(xiàn)消息的監(jiān)聽和發(fā)送業(yè)務(wù)。
3.7 注冊消息監(jiān)聽-setCharacteristicNotification
@SuppressLint("MissingPermission")
private void prepareBroadcastDataNotify(BluetoothGatt mBluetoothGatt, BluetoothGattCharacteristic characteristic) {
Log.e(TAG, "CharacteristicUUID:" + characteristic.getUuid().toString());
int charaProp = characteristic.getProperties();
//判斷屬性是否支持消息通知
if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
BluetoothGattDescriptor descriptor =
characteristic.getDescriptor(UUID.fromString(UUIDManager.READ_DEDSCRIPTION_UUID));
if (descriptor != null) {
//注冊消息通知
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
}
在上面的示例中:READ_DEDSCRIPTION_UUID = "00002902-0000-1000-8000-00805f9b34fb" 是固定的,不管你鏈接什么樣的藍牙設(shè)備。
在注冊消息監(jiān)聽,都是使用UUID值是00002902-0000-1000-8000-00805f9b34fb進行的。這個是Android系統(tǒng)保留的。用于動態(tài)監(jiān)聽的。
你如果不想使用這個動態(tài)監(jiān)聽。就需要自己寫線程主動去輪詢獲取到藍牙設(shè)備發(fā)送過來的消息了。
到這里,我們其實就能夠?qū)崿F(xiàn)藍牙設(shè)備的實時監(jiān)聽,并得到消息內(nèi)容了。
3.8 寫數(shù)據(jù)到藍牙設(shè)備中
我們?nèi)绻雽?nèi)容推送到藍牙設(shè)備中,在發(fā)現(xiàn)服務(wù)的時候onServicesDiscovered 遍歷特性中,確保是用于寫消息的特性對象后。選擇持有該特性,然后通過:
String data ="0x12";
BluetoothGattCharacteristic writeCharact = bluetoothGattService.
getCharacteristic(UUID.fromString(WRITE_UUID));
//查找UUID是寫的特性,并檢測是否擁有寫權(quán)限
if (writeCharact == null || writeCharact.getProperties() != BluetoothGattCharacteristic.PROPERTY_WRITE) {
return ;//該特性沒有寫的權(quán)限。所以無法傳入
}
// 當數(shù)據(jù)傳遞到藍牙之后
// 會回調(diào)BluetoothGattCallback里面的write方法
writeCharact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
// 將需要傳遞的數(shù)據(jù)轉(zhuǎn)為16進制數(shù)
writeCharact.setValue(data);
bluetoothGatt.writeCharacteristic(writeCharact);
3.9 關(guān)閉連接
當藍牙通訊結(jié)束,或者界面關(guān)閉時。我們需要關(guān)閉GATT服務(wù),減少資源占用。
if (bluetoothGatt != null) {
bluetoothGatt.close();
bluetoothGatt.disconnect();
bluetoothGatt = null;
}
也可以關(guān)閉BluetoothGattCallback 的回調(diào)監(jiān)聽:
gattCallback.disConnectBlue();//關(guān)閉GATT服務(wù)回調(diào)監(jiān)聽
4. 小結(jié)
到這里藍牙的鏈接和讀取就結(jié)束了。
我們通過bluetoothAdapter 查找到藍牙設(shè)備之后,再通過GATT服務(wù)進行藍牙設(shè)備與手機之間的配對。直接比對UUID,而不再需要PIN碼進行配對了。
(PS:有些安全性要求比較高的設(shè)備,還是會需要主動進行PIN碼配對。PIN配隊就只能通過系統(tǒng)設(shè)備界面中的藍牙功能項進行操作了。)
通過GATT服務(wù)連接成功后。就可以查詢該Server下的各種特性了,不同的特性對應(yīng)了一個功能。有發(fā)消息的特性,也有用于收消息的特性。
同時一個藍牙設(shè)備對象,可能有多種服務(wù)功能。
如果不想自己寫線程變量輪詢設(shè)備發(fā)送過來的消息,就通過注冊消息監(jiān)聽。讓BLE框架幫我們進行輪詢之后,再通知到我們。