精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Android BLE 藍(lán)牙開發(fā),連接藍(lán)牙設(shè)備進(jìn)行通訊

移動(dòng)開發(fā) Android
我們通過bluetoothAdapter 查找到藍(lán)牙設(shè)備之后,再通過GATT服務(wù)進(jìn)行藍(lán)牙設(shè)備與手機(jī)之間的配對(duì)。直接比對(duì)UUID,而不再需要PIN碼進(jìn)行配對(duì)了。

1. 介紹

本篇主要基于 Android 官方的低功耗藍(lán)牙連接服務(wù)。

講解如何通過 UUID 連接藍(lán)牙設(shè)備。如果你針對(duì) GATT 服務(wù)不太了解。那么這篇應(yīng)該能夠稍微幫助到你。

官方文檔地址:https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le?hl=zh_cn#connect

2. 概念

如果是老用戶了,那么就應(yīng)該知道曾經(jīng)藍(lán)牙設(shè)備是一個(gè)高耗電的部件。根本不可能長時(shí)間開啟。而在藍(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è)備也都開始大量支持藍(lán)牙通訊。因?yàn)槟芎暮统杀窘档土恕?/p>

針對(duì)低功耗藍(lán)牙通訊,Android 4.3(API 18)開始引入了 BLE 庫。我們可以直接使用 Android SDK 中的藍(lán)牙 BLE 庫,而不用額外導(dǎo)入依賴庫。

以前開發(fā)藍(lán)牙通訊,還需要實(shí)現(xiàn)藍(lán)牙配對(duì)。需要主動(dòng)跳轉(zhuǎn)到手機(jī)設(shè)置界面進(jìn)行PIN碼配對(duì),然后配對(duì)通過之后才能進(jìn)行藍(lán)牙鏈接。

而使用BLE庫,我們可以直接通過藍(lán)牙設(shè)備的UUID進(jìn)行連接(通過GATT服務(wù)),在當(dāng)前應(yīng)用內(nèi)就能直接連接了。而不用通過系統(tǒng)設(shè)置。

市面上的各種手環(huán)的自動(dòng)匹配鏈接,電子秤的自動(dòng)連接等等都是通過GATT進(jìn)行通訊和鏈接的。

2.1 術(shù)語

  • GATT:全稱為:Generic Attribute Profile,翻譯為:通用屬性配置文件。GATT 配置文件是一種通用規(guī)范,內(nèi)容針對(duì)在 BLE 鏈路上發(fā)送和接收稱為“屬性ATT”的簡(jiǎn)短數(shù)據(jù)片段。目前所有低功耗應(yīng)用配置文件均以 GATT 為基礎(chǔ)。
  • ATT:全稱為:Attribute Protocol,翻譯為:屬性協(xié)議。它是 GATT 的構(gòu)建基礎(chǔ),二者的關(guān)系也被稱為 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è)描述特征值的描述符。您可將特征理解為類型,后者與類類似。
  • 描述符:描述符是描述特征值的已定義屬性。例如,描述符可指定人類可讀的描述、特征值的可接受范圍或特定于特征值的度量單位。
  • Service — 服務(wù)是一系列特征。例如,您可能擁有名為“心率監(jiān)測(cè)器”的服務(wù),其中包括“心率測(cè)量”等特征。

以上術(shù)語的介紹來源于Android官網(wǎng)

2.2 通訊過程

假如我們有一個(gè)藍(lán)牙外置設(shè)備(Device),然后有一個(gè)支持藍(lán)牙的移動(dòng)設(shè)備(Phone)。兩者之間的通訊方式步驟是:

  1. Device 開啟藍(lán)牙。(通常這些設(shè)備都是開機(jī)之后,就默認(rèn)開啟藍(lán)牙了)
  2. Phone 開啟藍(lán)牙。
  3. Phone 發(fā)現(xiàn) Device。
  4. Phone 與 Device 創(chuàng)建藍(lán)牙連接。
  5. Phone 創(chuàng)建 Gatt 客戶端,與 Device Gatt 服務(wù)端連接。
  6. Phone 通過 Gatt 服務(wù)功能獲取 Device 中的消息,并發(fā)送消息給 Device 設(shè)備。

整個(gè)過程就是這樣的。下面我也將按照這個(gè)通訊過程進(jìn)行介紹。

3.開發(fā)

基于我的使用情況,從無到有的介紹,完整的藍(lán)牙開發(fā)配置過程。給大家一個(gè)參考

語言主要為 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)。如果不開啟 ACCESS_FINE_LOCATION 權(quán)限。那么我們將會(huì)無法發(fā)現(xiàn)藍(lán)牙設(shè)備。

也就是執(zhí)行藍(lán)牙掃描 API 無法得到任何結(jié)果(PS::Logcat 中的錯(cuò)誤日志會(huì)告訴你,要開啟位置權(quán)限,否則無法掃描發(fā)現(xiàn)藍(lán)牙設(shè)備)。

<!-- 藍(lán)牙搜索配對(duì) -->
<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" />
<!-- 操縱藍(lán)牙的開啟-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- 如果應(yīng)用必須安裝在支持藍(lán)牙的設(shè)備上,可以將下面的required的值設(shè)置為true。-->
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false" />

其中 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)牙鏈接,開關(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)限配置完畢之后,就是代碼開發(fā)了。

不管是高版本,還是低版本。將權(quán)限都申請(qǐng)可以說最穩(wěn)妥了。

3.2 檢測(cè)設(shè)備是否支持藍(lán)牙

通常情況下,手機(jī)是有藍(lán)牙的。而我們?nèi)绻谄渌?Android 系統(tǒng)的設(shè)備中,例如TV,平板,一體機(jī)等等。是否有藍(lán)牙還真不能完整保證。

如果不確定的情況下,那么可以通過以下代碼檢查 BLE 的可用性。

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
//不支持藍(lán)牙設(shè)備
finish();
} else {
//支持藍(lán)牙設(shè)備
}

藍(lán)牙是否開啟都不影響檢查結(jié)果。它檢查的是設(shè)備是否有藍(lán)牙功能,而不是藍(lán)牙是否啟動(dòng),下面會(huì)介紹如何判斷藍(lán)牙是否啟動(dòng)

3.3 開啟藍(lán)牙

當(dāng)我們?cè)O(shè)備也支持藍(lán)牙了,權(quán)限也配置了。下一步就是獲取 BluetoothAdapter 對(duì)象了。

final BluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();

我們后續(xù)控制藍(lán)牙的狀態(tài),都是通過該方法實(shí)現(xiàn)的。

首先,檢測(cè)藍(lán)牙是否開啟。可以通過isEnabled()方法進(jìn)行檢測(cè):

if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
//開啟設(shè)備的藍(lán)牙鏈接
bluetoothAdapter.enable();//開啟藍(lán)牙
//動(dòng)態(tài)判斷是否擁有位置權(quán)限ACCESS_COARSE_LOCATION 或ACCESS_FINE_LOCATION ,然后再執(zhí)行藍(lán)牙掃描
} else {
//動(dòng)態(tài)判斷是否擁有位置權(quán)限ACCESS_COARSE_LOCATION 或ACCESS_FINE_LOCATION,然后再執(zhí)行藍(lán)牙掃描
}

我們其實(shí)可以直接使用bluetoothAdapter.enable()開啟藍(lán)牙。當(dāng)藍(lán)牙沒有開啟時(shí),我們可以直接開啟藍(lán)牙。

這個(gè)方法的結(jié)果,并不是實(shí)時(shí)返回的。我們?nèi)绻浪{(lán)牙是否開啟,需要監(jiān)聽藍(lán)牙狀態(tài)的廣播才行。下面會(huì)介紹廣播監(jiān)聽。

PS:這個(gè)方法需要android.Manifest.permission.BLUETOOTH_CONNECT 權(quán)限才能使用。

官方是建議我們通過Intent讓系統(tǒng)設(shè)置進(jìn)行開啟藍(lán)牙的。

if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

但是現(xiàn)在startActivityForResult方法已經(jīng)過時(shí)。我們可以使用Launcher來調(diào)用:

ActivityResultLauncher<Intent> launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == RESULT_OK) {
//處理返回結(jié)果
}
});

上面的 launcher?需要在Activity? 的 onCreate 方法中初始化。然后在需要進(jìn)行藍(lán)牙設(shè)置界面啟動(dòng)的地方配置:

Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //創(chuàng)建一個(gè)藍(lán)牙啟動(dòng)的意圖
launcher.launch(enableBtIntent);//使用launcer啟動(dòng)這個(gè)意圖就可以了。

我們?nèi)绻褂胋luetoothAdapter.enable();?時(shí)Android Studio出現(xiàn)代碼錯(cuò)誤警告,可以在該代碼使用的方法中添加:@SuppressLint("MissingPermission")注解。

3.4 廣播監(jiān)聽

其實(shí)這個(gè)廣播監(jiān)聽,是否需要。根據(jù)大家實(shí)際情況來定。不一定需要。

首先,創(chuàng)建一個(gè)動(dòng)態(tài)廣播對(duì)象:

public class BluetoothFoundReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//監(jiān)聽藍(lá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è)備,每掃碼到一個(gè)設(shè)備,都會(huì)觸發(fā)一次
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//我們可以得到藍(lán)牙設(shè)備
} else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
//藍(lán)牙開關(guān)狀態(tài)

int statue = bluetoothAdapter.getState();
switch (statue) {
case BluetoothAdapter.STATE_OFF:
Log.e(TAG, "藍(lán)牙狀態(tài):,藍(lán)牙關(guān)閉");
break;
case BluetoothAdapter.STATE_ON:
Log.e(TAG, "藍(lán)牙狀態(tài):,藍(lán)牙打開");

break;
case BluetoothAdapter.STATE_TURNING_OFF:
Log.e(TAG, "藍(lán)牙狀態(tài):,藍(lán)牙正在關(guān)閉");
break;
case BluetoothAdapter.STATE_TURNING_ON:
Log.e(TAG, "藍(lán)牙狀態(tài):,藍(lán)牙正在打開");
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

然后進(jìn)行廣播注冊(cè):

bluetoothFoundReceiver = new BluetoothFoundReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//連接藍(lán)牙,斷開藍(lán)牙
filter.addAction(BluetoothDevice.ACTION_FOUND);//找到設(shè)備的廣播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索完成的廣播
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//狀態(tài)改變 配對(duì)開始時(shí),配對(duì)成功時(shí)
registerReceiver(bluetoothFoundReceiver, filter);

注冊(cè)完畢后,在onDestroy方法中需要注銷注冊(cè):

@Override
protected void onDestroy() {
if (bluetoothFoundReceiver != null)
unregisterReceiver(bluetoothFoundReceiver); //停止監(jiān)聽
super.onDestroy();
}

其實(shí),我們只需要藍(lán)牙狀態(tài)的監(jiān)聽就可以了BluetoothAdapter.ACTION_STATE_CHANGED 其他的設(shè)備查找,配對(duì)。可以不用,因?yàn)橛|發(fā)到廣播的設(shè)備查找效率太低,而且多次重復(fù)查找時(shí),還會(huì)出現(xiàn)耗時(shí)變長。設(shè)備無法查找到的情況。

3.5 藍(lán)牙設(shè)備查找

官方文檔上推薦的查找方式是:

bluetoothAdapter.startLeScan(leScanCallback);  //查找
bluetoothAdapter.stopLeScan(leScanCallback); //停止查找

可是現(xiàn)在這個(gè)方法也過時(shí)了。替換方法是:

BluetoothLeScanner scanner = bluetoothAdapter.getBluetoothLeScanner();
//不進(jìn)行權(quán)限驗(yà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, "搜索錯(cuò)誤" + errorCode);
}
};
scanner.startScan(callback);

onScanResult方法是一個(gè)在子線程觸發(fā)的回調(diào),我們不能在該方法中直接操作UI對(duì)象。

其次,掃描到一個(gè)藍(lán)牙設(shè)備就會(huì)觸發(fā)一次消息回調(diào)。我們可以得到一個(gè)BluetoothDevice對(duì)象。 也就是說這個(gè)方法中會(huì)觸發(fā)多次回調(diào),

所以建議,在掃描到我們的藍(lán)牙設(shè)備之后,主動(dòng)調(diào)用scanner.stopScan(callback);停止掃描。

PS:這種查找方式,不會(huì)觸發(fā)藍(lán)牙的遍歷廣播。我們?nèi)绻_啟廣播進(jìn)行監(jiān)聽設(shè)備掃描情況。如果通過startScan方法,廣播中不會(huì)有回調(diào)。

上面是一個(gè)通用搜索模式,我們還可以配置自己的過濾條件。例如:

ScanFilter sn =new ScanFilter.Builder().setDeviceName("藍(lán)牙設(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?對(duì)象,我們可以配置我們想查找的藍(lán)牙設(shè)備的信息。可以是setDeviceName,setServiceUuid,setDeviceAddress,setServiceSolicitationUuid等。

ScanSettings對(duì)象是可以定義我們的掃描模式,通過配置該項(xiàng)可以提高掃描效率。

默認(rèn)情況下,執(zhí)行的是:SCAN_MODE_LOW_POWER在低功耗模式下執(zhí)行藍(lán)牙LE掃描。 這是默認(rèn)的掃描模式,因?yàn)樗淖钌俚碾娏俊?/p>

3.5.1 startDiscovery

如果上面的方法還不滿足我們的情況,可以使用:

if (bluetoothAdapter.isDiscovering()) {//是否在掃描
bluetoothAdapter.cancelDiscovery(); //停止掃描
}
//查找藍(lán)牙
bluetoothAdapter.startDiscovery();

我們可以直接使用bluetoothAdapter進(jìn)行掃描。這個(gè)方法觸發(fā)之后是由系統(tǒng)進(jìn)行藍(lán)牙掃描。就和我們?cè)谑謾C(jī)的設(shè)置界面中點(diǎn)擊藍(lán)牙掃描一樣。

上面的這個(gè)方法沒有回調(diào),因?yàn)樗械乃{(lán)牙設(shè)備的發(fā)現(xiàn)都將通過廣播事件進(jìn)行傳遞。

需要通過我上面的廣播監(jiān)聽介紹的內(nèi)容。進(jìn)行實(shí)時(shí)獲取到掃描到的設(shè)備。

使用上面的方法有幾個(gè)缺點(diǎn):

1.效率慢,耗時(shí)很長。

2.重復(fù)掃描會(huì)失敗。不能說是失敗了,而是系統(tǒng)會(huì)將重復(fù)掃描的請(qǐng)求進(jìn)行阻止,關(guān)鍵的問題在于這個(gè)阻止操作是手機(jī)廠商定制的。

PS:不管是BluetoothLeScanner? 還是bluetoothAdapter.startDiscovery() 去查找藍(lán)牙設(shè)備。都不建議一直重復(fù)掃描。否則會(huì)出現(xiàn)無法掃描到設(shè)備,沒有任何掃描結(jié)果等等情況。因?yàn)閽呙枋且粋€(gè)耗時(shí)耗電的操作。

3.6 鏈接Gatt

當(dāng)我們掃描到了藍(lán)牙設(shè)備之后,就會(huì)獲取到BluetoothDevice?對(duì)象。然后我們通過BluetoothDevice?對(duì)象創(chuàng)建GATT服務(wù)進(jìn)行后續(xù)的藍(lán)牙通訊。

BluetoothDevice device;// 當(dāng)我們通過掃描得到device對(duì)象之后,進(jìn)行Gatt服務(wù)創(chuàng)建
BluetoothGatt bluetoothGatt = device.connectGatt(this, false, gattCallback);

第一個(gè)傳參context沒有什么可以介紹的。

第二個(gè)傳參autoConnect:是一個(gè)boolean值對(duì)象,false代表直接連接到藍(lán)牙設(shè)備。true代表在藍(lán)牙設(shè)備可用時(shí)自動(dòng)連接。

第三個(gè)參數(shù)BluetoothGattCallback 是Gatt服務(wù)的各種回調(diào)了。

我們通過gattCallback回調(diào)的內(nèi)容,來得到與藍(lán)牙設(shè)備的鏈接狀態(tài),數(shù)據(jù)通信內(nèi)容等。

下面來詳細(xì)介紹下BluetoothGattCallback對(duì)象的幾個(gè)方法。

String SERVICE_UUID="00000-000000-000000-000000";//這個(gè)是我要鏈接的藍(lán)牙設(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 在實(shí)際過程中,該方法并沒有調(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()); // 我們可以遍歷到該藍(lán)牙設(shè)備的全部Service對(duì)象。然后通過比較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);
}
}

//藍(lán)牙設(shè)備發(fā)送消息后的自動(dòng)監(jiān)聽
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// readUUID 是我要鏈接的藍(lán)牙設(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();
}
}
}
};

我們可以通過鏈接成功和鏈接斷開。來判斷我們當(dāng)前與藍(lán)牙設(shè)備的通訊狀態(tài)。

當(dāng)我們比對(duì)Service?的UUID成功之后, 我們就可以獲取Service的Characteristic對(duì)象。該對(duì)象也就是特征。通過注冊(cè)特征來實(shí)現(xiàn)消息的監(jiān)聽和發(fā)送業(yè)務(wù)。

3.7 注冊(cè)消息監(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) {
//注冊(cè)消息通知
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
}

在上面的示例中:READ_DEDSCRIPTION_UUID = "00002902-0000-1000-8000-00805f9b34fb" 是固定的,不管你鏈接什么樣的藍(lán)牙設(shè)備。

在注冊(cè)消息監(jiān)聽,都是使用UUID值是00002902-0000-1000-8000-00805f9b34fb進(jìn)行的。這個(gè)是Android系統(tǒng)保留的。用于動(dòng)態(tài)監(jiān)聽的。

你如果不想使用這個(gè)動(dòng)態(tài)監(jiān)聽。就需要自己寫線程主動(dòng)去輪詢獲取到藍(lán)牙設(shè)備發(fā)送過來的消息了。

到這里,我們其實(shí)就能夠?qū)崿F(xiàn)藍(lán)牙設(shè)備的實(shí)時(shí)監(jiān)聽,并得到消息內(nèi)容了。

3.8 寫數(shù)據(jù)到藍(lán)牙設(shè)備中

我們?nèi)绻雽?nèi)容推送到藍(lán)牙設(shè)備中,在發(fā)現(xiàn)服務(wù)的時(shí)候onServicesDiscovered 遍歷特性中,確保是用于寫消息的特性對(duì)象后。選擇持有該特性,然后通過:

String data ="0x12";
BluetoothGattCharacteristic writeCharact = bluetoothGattService.
getCharacteristic(UUID.fromString(WRITE_UUID));
//查找UUID是寫的特性,并檢測(cè)是否擁有寫權(quán)限
if (writeCharact == null || writeCharact.getProperties() != BluetoothGattCharacteristic.PROPERTY_WRITE) {
return ;//該特性沒有寫的權(quán)限。所以無法傳入
}
// 當(dāng)數(shù)據(jù)傳遞到藍(lán)牙之后
// 會(huì)回調(diào)BluetoothGattCallback里面的write方法
writeCharact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
// 將需要傳遞的數(shù)據(jù)轉(zhuǎn)為16進(jìn)制數(shù)
writeCharact.setValue(data);
bluetoothGatt.writeCharacteristic(writeCharact);

3.9 關(guān)閉連接

當(dāng)藍(lán)牙通訊結(jié)束,或者界面關(guān)閉時(shí)。我們需要關(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é)

到這里藍(lán)牙的鏈接和讀取就結(jié)束了。

我們通過bluetoothAdapter 查找到藍(lán)牙設(shè)備之后,再通過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ì)就只能通過系統(tǒng)設(shè)備界面中的藍(lán)牙功能項(xiàng)進(jìn)行操作了。)

通過GATT服務(wù)連接成功后。就可以查詢?cè)揝erver下的各種特性了,不同的特性對(duì)應(yīng)了一個(gè)功能。有發(fā)消息的特性,也有用于收消息的特性。

同時(shí)一個(gè)藍(lán)牙設(shè)備對(duì)象,可能有多種服務(wù)功能。

如果不想自己寫線程變量輪詢?cè)O(shè)備發(fā)送過來的消息,就通過注冊(cè)消息監(jiān)聽。讓BLE框架幫我們進(jìn)行輪詢之后,再通知到我們。

責(zé)任編輯:武曉燕 來源: zinyan
相關(guān)推薦

2023-04-17 16:10:14

鴻蒙藍(lán)牙

2015-09-22 11:04:24

藍(lán)牙4.0開發(fā)

2022-01-25 16:54:14

BLE操作系統(tǒng)鴻蒙

2021-10-30 07:55:00

BLE 藍(lán)牙開發(fā)

2022-11-17 15:26:06

低功耗藍(lán)牙鴻蒙

2015-09-11 15:41:08

2015-02-27 16:03:26

Android源碼Bluetooth_4BLE藍(lán)牙通信

2011-06-08 12:42:08

Android 藍(lán)牙

2021-11-12 23:44:28

Windows 10Windows微軟

2023-09-19 15:58:13

Zigbee藍(lán)牙

2015-01-09 16:10:19

藍(lán)牙設(shè)備安全安全工具BlueMaho

2020-09-17 11:02:40

BLESA藍(lán)牙攻擊漏洞

2019-01-10 18:22:58

2010-01-08 15:35:30

Ubuntu連接

2021-09-05 05:59:00

BrakTooth漏洞藍(lán)牙設(shè)備

2021-12-17 11:29:03

WiFi漏洞芯片

2020-05-20 12:52:03

漏洞攻擊藍(lán)牙

2013-12-09 09:37:11

藍(lán)牙4.1物聯(lián)網(wǎng)

2024-04-12 15:52:42

藍(lán)牙

2021-09-07 05:36:59

藍(lán)牙漏洞惡意代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

一区二区三区成人| 精品一二三四在线| 精品视频久久久久久久| 日本在线观看a| 自拍视频在线免费观看| 激情综合色综合久久综合| 欧美精品videosex性欧美| 亚洲天堂资源在线| 日本欧美在线| 欧美日韩精品中文字幕| 国产高清精品软男同| 午夜成人免费影院| 国产在线一区二区综合免费视频| 91精品国产91久久久久| 国产喷水在线观看| 亚洲理论电影片| 日韩一区二区视频| 黄色片在线免费| av免费不卡国产观看| 中文字幕精品—区二区四季| 国产精品欧美久久| 91亚洲视频在线观看| 国产精品夜夜夜| 欧美风情在线观看| 我要看黄色一级片| 精品亚洲成人| 亚洲精品中文字幕av| 男人添女人荫蒂国产| 高清欧美日韩| 色婷婷av一区二区三区之一色屋| 特级西西444| 免费的黄网站在线观看| 国产清纯白嫩初高生在线观看91 | 久久99精品久久久野外观看| 色综合天天视频在线观看 | 91桃色在线| 亚洲蜜臀av乱码久久精品| 五月天亚洲综合小说网| 男人av在线| 99久久精品国产一区二区三区| 91欧美视频网站| 夜夜狠狠擅视频| 久久99热这里只有精品| 国产精品久久久久影院日本| 人人爽人人爽人人片av| 999亚洲国产精| 国内精品小视频在线观看| 欧美性猛交xxxxx少妇| 国产精品99久久久久久动医院| 国产一区二区欧美日韩| 国产精品久久久久无码av色戒| 日韩在线影视| 日韩精品中文字幕视频在线| 星空大象在线观看免费播放| 九色丨蝌蚪丨成人| 亚洲福利在线播放| 精品无人乱码一区二区三区的优势| 国产精品一区二区免费视频 | 久久久久久无码精品人妻一区二区| 日日夜夜一区| 在线成人免费视频| 国产又黄又猛的视频| 久久爱.com| 制服丝袜亚洲精品中文字幕| 欧美高清精品一区二区| 国产一精品一av一免费爽爽| 日韩一区二区精品在线观看| gogo亚洲国模私拍人体| 8848成人影院| 日韩精品免费在线| 一区二区黄色片| 日韩精品午夜| 欧美成人精品不卡视频在线观看| 久草视频免费播放| 国产欧美一级| 国产精品精品久久久久久| 伊人免费在线观看高清版| 精品一区二区三区影院在线午夜| 2014亚洲精品| 污视频网站免费观看| 国产亚洲女人久久久久毛片| 亚洲成年人专区| 成人女同在线观看| 色域天天综合网| 国产高清999| 精品久久ai电影| 在线精品国产成人综合| 成年人午夜剧场| 国产欧美日本| 成人妇女免费播放久久久| 亚洲免费视频网| 91丨九色丨黑人外教| 亚洲午夜精品久久久久久浪潮| 亚洲精品白浆| 色婷婷久久99综合精品jk白丝| 欧美美女一级片| 99a精品视频在线观看| 亚洲欧美成人网| 丝袜美腿小色网| 久久婷婷亚洲| 肥熟一91porny丨九色丨| 免费一级毛片在线观看| 亚洲摸摸操操av| 国产精品无码专区av在线播放| 四虎影视成人精品国库在线观看 | 在线亚洲观看| 国产三级精品网站| 无码国产精品高潮久久99| 国产精品乱码人人做人人爱| 水蜜桃色314在线观看| 久久久精品一区二区毛片免费看| 亚洲成人免费网站| 青花影视在线观看免费高清| 日韩黄色在线观看| 精品国产一区二区三区久久久久久| 国产大片在线免费观看| 午夜精品久久久久| 成人av毛片在线观看| 九一成人免费视频| 性视频1819p久久| 99国产精品99| 中文字幕中文字幕一区二区| 人妻熟女一二三区夜夜爱| 中文字幕一区二区三区四区久久| 最新69国产成人精品视频免费| 六月丁香激情综合| 成人综合在线观看| 国产日产欧美一区二区| 欧美亚洲二区| 国产亚洲视频在线| caoporn国产| a亚洲天堂av| 超碰97国产在线| av大片在线看| 91国产视频在线观看| 亚洲精品女人久久久| 国产精品www994| 4444kk亚洲人成电影在线| 午夜精品一区| 欧美日韩黄色一区二区| 国产免费一区二区三区网站免费| 亚洲在线观看| 久久av免费一区| 日韩欧美精品一区二区三区| 亚洲国产精久久久久久久| 国产亚洲欧美精品久久久久久| 国产在线不卡视频| 400部精品国偷自产在线观看| 国产美女视频一区二区| 久久精品视频网站| 97人妻精品一区二区三区视频| 国产欧美精品一区aⅴ影院| 亚洲五月天综合| 精品国产乱码久久久久久1区2匹| 国产suv精品一区二区| 国产爆初菊在线观看免费视频网站 | 99免费精品在线观看| 免费在线观看视频a| 欧美激情久久久久久久久久久| 91av网站在线播放| 邻居大乳一区二区三区| 91国产免费观看| 日韩亚洲欧美中文字幕| 韩国v欧美v日本v亚洲v| 午夜久久久久久久久久久| 9999久久久久| 青草热久免费精品视频| 成av人电影在线观看| 欧美美女一区二区| 九九九免费视频| 91麻豆免费在线观看| 熟女少妇精品一区二区| 日韩在线观看一区 | 国产一二三四在线视频| 91精品婷婷色在线观看| 国产99视频精品免费视频36| 蜜桃视频动漫在线播放| 在线播放日韩欧美| 国产区精品在线| 五月天欧美精品| 免费看裸体网站| 国产一区二区三区在线看麻豆 | 久久久亚洲人| 国产系列第一页| 女同久久另类99精品国产| 国产激情视频一区| 91麻豆免费在线视频| 亚洲成色777777在线观看影院| 69视频免费看| 亚洲精品欧美在线| 深爱五月激情网| 国产综合久久久久久久久久久久 | 美女视频一区二区三区| 少妇一晚三次一区二区三区| 国产99久久久国产精品成人免费| 成人福利视频网| 在线观看爽视频| 久久久www成人免费精品| 四虎精品在永久在线观看 | 精品88久久久久88久久久| 69国产精品视频免费观看| 亚洲视频在线一区观看| 人妻大战黑人白浆狂泄| 国产精品18久久久久久vr| 999精品网站| 夜夜嗨av一区二区三区网站四季av| 亚洲成人自拍视频| 欧美日韩另类图片| 91性高湖久久久久久久久_久久99| 日本三级一区| 欧美黑人性猛交| 欧美r级在线| 亚洲男女自偷自拍图片另类| 亚洲免费一级片| 欧美一区午夜视频在线观看| 免费黄色片视频| 调教+趴+乳夹+国产+精品| 蜜桃视频最新网址| 国产亚洲精品久| 久久久久亚洲AV成人无码国产| 国产在线日韩欧美| 韩国视频一区二区三区| 久久精品男女| 欧美精品一区免费| 亚洲欧洲一区| 国产九色porny| 欧美va亚洲va日韩∨a综合色| 午夜免费电影一区在线观看| 蜜桃精品噜噜噜成人av| 国产精品区一区二区三在线播放 | 亚洲美女www午夜| 日日躁夜夜躁白天躁晚上躁91| 日韩一区二区免费电影| 国产露脸国语对白在线| 欧美日韩亚洲综合在线| wwwwww在线观看| 日本高清不卡在线观看| 欧美日韩精品区| 精品毛片网大全| 日韩欧美亚洲视频| 五月婷婷综合在线| 国产精品suv一区二区| 亚洲午夜一区二区三区| 久久久久久久9999| 亚洲国产精品麻豆| 伊人国产在线观看| 午夜视频在线观看一区二区三区| 日本熟妇色xxxxx日本免费看| 亚洲一级二级三级在线免费观看| 久久久久久久中文字幕| 亚洲一区在线播放| 国产污片在线观看| 精品久久久久久久久久久久久久| 国产女同在线观看| 欧美视频在线免费看| 特级毛片www| 在线免费不卡视频| 在线观看免费高清视频| 欧美一区二区三区播放老司机| 国产激情视频在线播放| 精品美女在线播放| 视频三区在线观看| 亚洲色图欧美制服丝袜另类第一页| 国产中文字幕在线播放| 色多多国产成人永久免费网站| 香蕉视频网站在线观看| 美女精品视频一区| 大黄网站在线观看| 国产精品大片wwwwww| 亚洲精品伊人| 国产另类自拍| 精品不卡一区| 欧美性受xxxx黑人猛交88| 亚洲私拍自拍| 99免费视频观看| 极品少妇xxxx精品少妇偷拍| youjizz.com国产| 久久久亚洲午夜电影| 少妇视频一区二区| 伊人一区二区三区| 国产成人综合欧美精品久久| 欧美在线视频全部完| jlzzjlzzjlzz亚洲人| 国产丝袜一区二区三区| 老司机在线看片网av| 97久久超碰福利国产精品…| av成人免费| 成人午夜电影在线播放| 精品国产一区二区三区久久久蜜臀| 黄色污污在线观看| 噜噜噜躁狠狠躁狠狠精品视频| 99国产精品久久久久久| 成人18视频在线播放| 精品人体无码一区二区三区| 亚洲.国产.中文慕字在线| 中文无码精品一区二区三区 | 电影av在线| 欧美激情在线观看视频| 日韩毛片一区| 激情五月综合色婷婷一区二区| 日韩电影一区| 天天夜碰日日摸日日澡性色av| 久久精品国产99久久6| www.超碰97| 亚洲精品欧美激情| 中文字幕精品在线观看| 亚洲成人亚洲激情| 91精品久久| 国产精品极品美女粉嫩高清在线| 欧美挤奶吃奶水xxxxx| 国产情侣第一页| 激情综合网最新| 激情无码人妻又粗又大| 日韩欧美在线第一页| 成人午夜精品福利免费| 麻豆国产va免费精品高清在线| 久久天堂av| 欧美亚洲免费高清在线观看| 黄色欧美日韩| 韩国一区二区三区美女美女秀| 精品无码一区二区三区电影桃花| 国产日本精品| 国产精品99导航| 丰满熟妇乱又伦| 亚洲欧美激情视频在线观看一区二区三区| 国产一区免费观看| 全色精品综合影院| 成人动漫精品一区二区| 日本一区二区在线播放| 三级黄色录像视频| 欧美午夜在线视频| 中文字幕欧美人妻精品一区| 久久综合久久综合久久综合| 香蕉视频一区二区| 精品国产一区a| 欧美aaa免费| 亚洲最大成人免费视频| 91精品国产麻豆国产在线观看 | 丁香影院在线| 91最新在线免费观看| 久久久国产精品| 国产又黄又猛的视频| 国产精品国产三级国产aⅴ原创 | 日韩在线影院| 欧美日本亚洲| 久热精品视频| 国产三级黄色片| 欧美日韩电影一区| 日本三级视频在线观看| 成人精品aaaa网站| 希岛爱理一区二区三区| 性鲍视频在线观看| 亚洲精品成人在线| 老熟妇高潮一区二区高清视频| 性色av一区二区三区| 亚洲精华一区二区三区| 日本中文字幕片| 国产精品三级电影| 国产女18毛片多18精品| 欧美激情综合色综合啪啪五月| 乱亲女h秽乱长久久久| 99精品人妻少妇一区二区| 久久老女人爱爱| 亚洲一区二区视频在线播放| 久久综合久久八八| 草莓视频一区二区三区| 久久综合九色综合88i| 国产调教视频一区| 国产精品久久无码一三区| 色综合天天综合网国产成人网 | www..com国产| 一区二区三欧美| 国产激情一区| 欧美深夜福利视频| 国产欧美一区二区三区在线看蜜臀| 91久久精品国产91性色69| 欧美大荫蒂xxx| 国产一区二区电影在线观看| 国产精品自在自线| 亚洲成年人网站在线观看| 可以直接在线观看的av| 91九色国产社区在线观看| 影院欧美亚洲| 国产精品酒店视频| 精品国产污网站| 国产成人精品一区二区三区视频 | 日韩电影一区二区三区四区| 91视频青青草| 亚洲乱亚洲乱妇无码| 欧美日本三级| 欧美精品无码一区二区三区| 一区二区三区在线免费观看| 四虎影视精品成人| 91嫩草在线| 日本美女视频一区二区| 国产一级久久久| 久久久国产一区二区| 自拍视频一区| av在线天堂网| 精品视频在线免费观看|