鴻蒙BLE藍(lán)牙通信開(kāi)發(fā)總結(jié)
原創(chuàng)??想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)??
??https://harmonyos.51cto.com??
目標(biāo)
通過(guò)BLE掃描和廣播提供的開(kāi)放能力,可以根據(jù)指定狀態(tài)獲取外圍設(shè)備、啟動(dòng)或停止BLE掃描、廣播、數(shù)據(jù)交互。
關(guān)于BLE藍(lán)牙的掃描和廣播你可以查看官方文檔
效果
藍(lán)牙介紹
藍(lán)牙是短距離無(wú)線通信的一種方式,支持藍(lán)牙的兩個(gè)設(shè)備必須配對(duì)后才能通信。HarmonyOS藍(lán)牙主要分為傳統(tǒng)藍(lán)牙和低功耗藍(lán)牙(通常稱為BLE,Bluetooth Low Energy)。傳統(tǒng)藍(lán)牙指的是藍(lán)牙版本3.0以下的藍(lán)牙,低功耗藍(lán)牙指的是藍(lán)牙版本4.0以上的藍(lán)牙。
如果你對(duì)藍(lán)牙感興趣,可以看看 HarmonyOS 藍(lán)牙介紹或者我前面寫的一篇文章:鴻蒙關(guān)于藍(lán)牙的那些事
概念
在進(jìn)入實(shí)戰(zhàn)之前,先說(shuō)明一個(gè)BLE藍(lán)牙的通信協(xié)議,GATT【Generic Attribute Profile】,GATT 是一個(gè)在藍(lán)牙連接之上的發(fā)送和接收很短的數(shù)據(jù)段的通用規(guī)范,這些很短的數(shù)據(jù)段被稱為屬性(Attribute)。在說(shuō)明GATT之前還需要知道一個(gè)GAP【Generic Access Profile】。
GAP包含:搜索藍(lán)牙設(shè)備(Discovery)、管理連接(Link establishment),還有不同的安全等級(jí)(Security)。以及從用戶層面訪問(wèn)一些參數(shù)的方式。GAP給設(shè)備定義了若干角色,其中主要的兩個(gè)是:外圍設(shè)備(Peripheral)和中心設(shè)備(Central)。
- 外圍設(shè)備:這一般就是非常小或者簡(jiǎn)單的低功耗設(shè)備,用來(lái)提供數(shù)據(jù),并連接到一個(gè)更加相對(duì)強(qiáng)大的中心設(shè)備,例如:藍(lán)牙手環(huán)。
- 中心設(shè)備:中心設(shè)備相對(duì)比較強(qiáng)大,用來(lái)連接其他外圍設(shè)備,例如手機(jī)。
GATT定義兩個(gè)BLE設(shè)備通過(guò)叫做Service和Characteristic的東西進(jìn)行通信,他使用了ATT(Attribute Protocol)協(xié)議,需要說(shuō)明的是,GATT連接必需先經(jīng)過(guò)GAP協(xié)議。
另外,特別注意的是:GATT連接是獨(dú)占的。也就是一個(gè)BLE外設(shè)同時(shí)只能被一個(gè)中心設(shè)備連接。一旦外設(shè)被連接,它就會(huì)馬上停止廣播,這樣它就對(duì)其他設(shè)備不可見(jiàn)了。當(dāng)設(shè)備斷開(kāi),它又開(kāi)始廣播。中心設(shè)備和外設(shè)需要雙向通信的話,唯一的方式就是建立GATT連接。
GATT連接的網(wǎng)絡(luò)拓?fù)?/h4>
一個(gè)外設(shè)只能連接一個(gè)中心設(shè)備,而一個(gè)中心設(shè)備可以連接多個(gè)外設(shè)。中心設(shè)備負(fù)責(zé)掃描外圍設(shè)備、發(fā)現(xiàn)廣播。外圍設(shè)備負(fù)責(zé)發(fā)送廣播。
前置條件
一、前期準(zhǔn)備
說(shuō)明:如果需要完成藍(lán)牙間的通信則需要借助藍(lán)牙中的服務(wù),如何獲取BLE藍(lán)牙相關(guān)的MAC地址和服務(wù)編號(hào)【uuid】可以參看我前面寫的一篇文章:鴻蒙關(guān)于藍(lán)牙的那些事
1.1、獲取外圍藍(lán)牙設(shè)備的MAC
本此講解的實(shí)戰(zhàn)中使用到的相關(guān)設(shè)備MAC
MAC:E2:xx:xx:xx:xx:EB
1.2、獲取服務(wù)編號(hào)【uuid】
本此講解的實(shí)戰(zhàn)中使用到的相關(guān)設(shè)備UUID
- Service:6e40xxxx-xxxx-xxxx-e0a9-e50e24dcca9e
- Notify:6e40xxxx-xxxx-xxxx-e0a9-e50e24dcca9e
業(yè)務(wù)邏輯梳理
權(quán)限問(wèn)題,首先需要注冊(cè)藍(lán)牙相關(guān)權(quán)限;
搜索藍(lán)牙,應(yīng)用啟動(dòng)后可以手動(dòng)的開(kāi)啟和關(guān)閉藍(lán)牙掃描;
連接藍(lán)牙,根據(jù)藍(lán)牙的mac地址,調(diào)用connect進(jìn)行連接;
遍歷藍(lán)牙特征,在藍(lán)牙連接成功后,獲取藍(lán)牙的服務(wù)特征,設(shè)置指定GATT特征通知;
通知數(shù)據(jù),將數(shù)據(jù)通過(guò)藍(lán)牙服務(wù)中的通知屬性發(fā)送;
接受通知,中心設(shè)備通過(guò)characteristicChangedEvent接收通知數(shù)據(jù),并顯示在屏幕上;
關(guān)閉藍(lán)牙,在應(yīng)用推出后,需要釋放資源,斷開(kāi)連接。
實(shí)戰(zhàn):BLE藍(lán)牙設(shè)備間的數(shù)據(jù)交互–中心設(shè)備接收外圍設(shè)備的通知數(shù)據(jù)
一、創(chuàng)建項(xiàng)目
說(shuō)明:通過(guò)DevEco Studio創(chuàng)建Application項(xiàng)目(java)。
二、權(quán)限
2.1、聲明權(quán)限
說(shuō)明:在項(xiàng)目的config.json中聲明操作藍(lán)牙必要的權(quán)限。
- ohos.permission.USE_BLUETOOTH:允許應(yīng)用查看藍(lán)牙的配置。
- ohos.permission.DISCOVER_BLUETOOTH:允許應(yīng)用配置本地藍(lán)牙,并允許其查找遠(yuǎn)端設(shè)備且與之配對(duì)連接。
- ohos.permission.LOCATION:允許應(yīng)用在前臺(tái)運(yùn)行時(shí)獲取位置信息。
代碼如下:
"reqPermissions": [
{
"name": "ohos.permission.USE_BLUETOOTH"
},
{
"name": "ohos.permission.DISCOVER_BLUETOOTH"
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:permreason_location",
"usedScene": {
"ability": [
".MainAbility"
],
"when": "inuse"
}
}
],
2.2、顯式聲明敏感權(quán)限
說(shuō)明:ohos.permission.LOCATION屬于敏感權(quán)限,需要在代碼中顯式聲明。在MainAbility中動(dòng)態(tài)申請(qǐng)權(quán)限,代碼如下:
private final String PERMISSION_LOCATION = "ohos.permission.LOCATION";
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilitySlice.class.getName());
// 判斷權(quán)限是否已授予
if (verifySelfPermission(PERMISSION_LOCATION) != IBundleManager.PERMISSION_GRANTED) {
// 應(yīng)用未被授權(quán)
if (canRequestPermission(PERMISSION_LOCATION)) {
// 是否可以申請(qǐng)彈窗授權(quán)
requestPermissionsFromUser(new String[]{PERMISSION_LOCATION}, 0);
} else {
// 顯示應(yīng)用需要權(quán)限的理由,提示用戶進(jìn)入設(shè)置授權(quán)
new ToastDialog(getContext()).setText("請(qǐng)進(jìn)入系統(tǒng)設(shè)置進(jìn)行授權(quán)").show();
}
}
}
三、啟動(dòng)藍(lán)牙
說(shuō)明:如果藍(lán)牙處于關(guān)閉狀態(tài),請(qǐng)求將設(shè)備的藍(lán)牙開(kāi)啟,代碼如下:
BluetoothHost bluetoothHost = BluetoothHost.getDefaultHost(this);
if (bluetoothHost.getBtState() != BluetoothHost.STATE_ON) {
bluetoothHost.enableBt();
}
四、中心設(shè)備進(jìn)行BLE掃描
4.1、API說(shuō)明
4.1.1、BLE中心設(shè)備管理類:BleCentralManager
4.1.2、中心設(shè)備管理回調(diào)類:BleCentralManagerCallback
4.2、掃描回調(diào)的處理
BLE掃描之前要先實(shí)現(xiàn)掃描回調(diào):BleCentralManagerCallback的接口,在scanResultEvent回調(diào)中可以獲取你需要的外圍設(shè)備實(shí)例,代碼如下:
BlePeripheralDevice mPeripheralDevice = null;
/**
* 實(shí)現(xiàn)中心設(shè)備管理回調(diào)
*/
public class MyBleCentralManagerCallback implements BleCentralManagerCallback {
/**
* 掃碼結(jié)果回調(diào)
* @param bleScanResult 掃碼結(jié)果
*/
@Override
public void scanResultEvent(BleScanResult bleScanResult) {
// 根據(jù)掃碼結(jié)果獲取外圍設(shè)備實(shí)例
mPeripheralDevice = bleScanResult.getPeripheralDevice();
}
/**
* 掃描失敗回調(diào)
* @param i
*/
@Override
public void scanFailedEvent(int i) {
}
/**
* 組掃描成功回調(diào)
* @param list 組信息
*/
@Override
public void groupScanResultsEvent(List<BleScanResult> list) {
}
}
4.3、獲取中心設(shè)備管理對(duì)象
調(diào)用BleCentralManager(BleCentralManagerCallback callback)接口獲取中心設(shè)備管理對(duì)象。代碼如下:
MyBleCentralManagerCallback myCentralManagerCallback = new MyBleCentralManagerCallback();
BleCentralManager mCentralManager = new BleCentralManager(this, myCentralManagerCallback);
4.4、啟動(dòng)掃描
調(diào)用startScan()開(kāi)始掃描BLE設(shè)備,在回調(diào)【BleCentralManagerCallback】中獲取掃描到的BLE設(shè)備。
值得一提的是,啟動(dòng)掃描接口中,可以傳入藍(lán)牙掃描過(guò)濾器【BleScanFilter】,如果過(guò)濾器為空,則不過(guò)濾。
下面使用指定的MAC地址進(jìn)行過(guò)濾啟動(dòng)藍(lán)牙掃描,代碼如下:
// 開(kāi)始掃描(過(guò)濾器指定的)設(shè)備
BleScanFilter bleScanFilter = new BleScanFilter();
bleScanFilter.setPeripheralAddress("E2:XX:XX:XX:XX:EB");// 替換你需要過(guò)濾的MAC
mFilters.add(bleScanFilter);
mCentralManager.startScan(mFilters);
五、藍(lán)牙連接
5.1、API
5.1.1、BLE藍(lán)牙外圍設(shè)備操作類:BlePeripheralDevice相關(guān)接口說(shuō)明
5.1.2、BLE藍(lán)牙外圍設(shè)備操作回調(diào)類:BlePeripheralCallback相關(guān)接口說(shuō)明
5.2、實(shí)現(xiàn)外圍設(shè)備操作回調(diào):BlePeripheralCallback,部分代碼如下:
/**
* 實(shí)現(xiàn)外圍設(shè)備操作回調(diào)
* 中心設(shè)備作為GattService的客戶端
*/
private class MyBlePeripheralCallback extends BlePeripheralCallback {
// TODO 回調(diào)接口實(shí)現(xiàn)包括:connectionStateChangeEvent、servicesDiscoveredEvent、characteristicChangedEvent等
}
在回調(diào)的接口中可以做三件事。
1、在connectionStateChangeEvent回調(diào)接口中,如果GATT連接成功,則可以調(diào)用mPeripheralDevice.discoverServices()獲取外圍設(shè)備支持的 Services、Characteristics 等特征值,在回調(diào) servicesDiscoveredEvent(int status) 中獲取外圍設(shè)備支持的服務(wù)和特征值,并根據(jù) UUID 判斷是什么服務(wù)。代碼如下:
/**
* 連接狀態(tài)變更
* 連接成功后可以在中心設(shè)備上(客戶端)發(fā)現(xiàn)GattService
* @param connectionState
*/
@Override
public void connectionStateChangeEvent(int connectionState) {
super.connectionStateChangeEvent(connectionState);
HiLog.info(label, "connectionState:" + connectionState);
if (connectionState == ProfileBase.STATE_CONNECTED && !isConnected) {
isConnected = true;
mPeripheralDevice.discoverServices();// 與外圍設(shè)備連接成功,發(fā)現(xiàn)GattService
setText(mTvStatus, "連接狀態(tài):已連接");
} else if (connectionState == ProfileBase.STATE_DISCONNECTED) {
// 斷開(kāi)連接
setText(mTvStatus, "連接狀態(tài):未連接");
}
}
2、在servicesDiscoveredEvent回調(diào)接口中,如果Service獲取成功,則根據(jù)獲取到的服務(wù)和特征值,調(diào)用 setNotifyCharacteristic設(shè)置指定GATT特征通知。代碼如下:
/**
* 在中心設(shè)備上發(fā)現(xiàn)服務(wù)(GattService外圍設(shè)備)的回調(diào)
* @param status 狀態(tài)
*/
@Override
public void servicesDiscoveredEvent(int status) { // 外圍設(shè)備服務(wù)發(fā)生更新觸發(fā)的回調(diào)。
if (status == BlePeripheralDevice.OPERATION_SUCC) {
HiLog.info(label, "servicesDiscoveredEvent OPERATION_SUCC");
List<GattService> services = mPeripheralDevice.getServices(); // 獲取Service成功后獲服務(wù)列表
for (GattService service : services) {
// 對(duì)每個(gè)服務(wù)進(jìn)行相應(yīng)操作
if (service.getUuid().equals(UUID.fromString(Constant.SERVICE_UUID))) {
HiLog.info(label, "servicesDiscoveredEvent 找到服務(wù)");
mPeripheralDevice.setNotifyCharacteristic
(service.getCharacteristic(UUID.fromString(Constant.NOTIFY_CHARACTER_UUID)).get(), true);
}
}
}
}
3、在characteristicChangedEvent回調(diào)接口中處理外圍設(shè)備特征的通知,可以從中獲取到通知的數(shù)據(jù)。代碼如下:
/**
* 特性變更的回調(diào)
* 接受外圍設(shè)備發(fā)送的數(shù)據(jù)
* @param characteristic
*/
@Override
public void characteristicChangedEvent(GattCharacteristic characteristic) {
super.characteristicChangedEvent(characteristic);
// 更新外圍設(shè)備發(fā)送的數(shù)據(jù)
String msg = new String(characteristic.getValue());
HiLog.info(label, "characteristicChangedEvent msg=" + msg);
setText(mTvData, msg);
}
5.3、設(shè)備藍(lán)牙連接
說(shuō)明:中心設(shè)備與外圍設(shè)備建立連接,調(diào)用connect(boolean isAutoConnect, BlePeripheraCallback callback)建立與外圍BLE設(shè)備的GATT連接,boolean參數(shù)isAutoConnect用于設(shè)置是否允許設(shè)備在可發(fā)現(xiàn)距離內(nèi)自動(dòng)建立GATT連接。代碼如下:
MyBlePeripheralCallback mPeripheralCallback = new MyBlePeripheralCallback();
mPeripheralDevice.connect(false, mPeripheralCallback);
六、常量
說(shuō)明:Constant是用于定義常量類,其中定義了業(yè)務(wù)中需要使用的常量。其中"X"需要替換成你的藍(lán)牙設(shè)備信息。
public static final String PERIPHERAL_ADDRESS = "E2:XX:XX:XX:XX:EB";// 藍(lán)牙MAC
public static final String SERVICE_UUID = "6eXXXXXX-XXXX-XXXX-XXXX-e50e24dcca9e";// 藍(lán)牙的服務(wù)編號(hào)
public static final String NOTIFY_CHARACTER_UUID = "6eXXXXXX-XXXX-XXXX-XXXX-e50e24dcca9e";// 藍(lán)牙特性通知屬性的編號(hào)
到目前為止,就完成了中心設(shè)備與外圍設(shè)備的連接和相關(guān)的監(jiān)聽(tīng)回調(diào),當(dāng)外圍設(shè)備通過(guò)NOTIFY_CHARACTER_UUID發(fā)送的通知在外圍設(shè)備操作回調(diào)接口characteristicChangedEvent中就能監(jiān)聽(tīng)到變更,在參數(shù)GattCharacteristic中就可以獲取到通知中的數(shù)據(jù)內(nèi)容。
七、代碼
7.1、BLE藍(lán)牙中心設(shè)備的完整代碼
package xxx;
import com.nlscan.bluetoothassistant.ResourceTable;
import com.nlscan.bluetoothassistant.common.Constant;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.window.dialog.ToastDialog;
import ohos.agp.window.service.WindowManager;
import ohos.bluetooth.ProfileBase;
import ohos.bluetooth.ble.*;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MainAbilitySlice extends AbilitySlice implements Component.ClickedListener{
private static final HiLogLabel label = new HiLogLabel(HiLog.LOG_APP, 0x00201, "BleCentralAbilitySlice");
public BlePeripheralDevice mPeripheralDevice;// 外圍設(shè)備實(shí)例
public GattCharacteristic mWriteCharacteristic;
private MyBlePeripheralCallback mPeripheralCallback;// 外圍設(shè)備操作回調(diào)
private MyBleCentralManagerCallback myCentralManagerCallback;// 中心設(shè)備管理器回調(diào)
private BleCentralManager mCentralManager;// 中心設(shè)備管理器
private List<BleScanFilter> mFilters;// 掃描過(guò)濾器
public boolean isConnected = false;// 是否已連接
private boolean isScanning = false;// 是否正在掃描
// 容器
private Text mTvStatus;// 狀態(tài)
private Text mTvData;// 數(shù)據(jù)
private Text mTvDevice;// 設(shè)備
private Button mBtnScan;// 掃描
private Button mBtnConnect;// 連接
private Button mBtnSend;// 發(fā)送
private TextField mTfInput;// 內(nèi)容輸入框
private Text mTvName;// 設(shè)備名稱
private Image mIvBle;// 藍(lán)牙圖標(biāo)
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
// 隱藏狀態(tài)欄、設(shè)置狀態(tài)欄和導(dǎo)航欄透明
getWindow().addFlags(WindowManager.LayoutConfig.MARK_FULL_SCREEN|
WindowManager.LayoutConfig.MARK_TRANSLUCENT_STATUS|
WindowManager.LayoutConfig.MARK_TRANSLUCENT_NAVIGATION);
initData();
initComponent();
initListener();
}
private void initData() {
mPeripheralCallback = new MyBlePeripheralCallback();
myCentralManagerCallback = new MyBleCentralManagerCallback();
mCentralManager = new BleCentralManager(this, myCentralManagerCallback);
mFilters = new ArrayList<>();
}
private void initComponent() {
mTvDevice = (Text) findComponentById(ResourceTable.Id_device_info);
mBtnScan = (Button) findComponentById(ResourceTable.Id_scan);
mTvStatus = (Text) findComponentById(ResourceTable.Id_status);
mBtnConnect = (Button) findComponentById(ResourceTable.Id_connect);
mTvData = (Text) findComponentById(ResourceTable.Id_data);
mBtnSend = (Button) findComponentById(ResourceTable.Id_send);
mTfInput = (TextField) findComponentById(ResourceTable.Id_input);
mTvName = (Text) findComponentById(ResourceTable.Id_device_name);
mIvBle = (Image) findComponentById(ResourceTable.Id_ble_image);
}
private void initListener() {
mBtnScan.setClickedListener(this);
mBtnConnect.setClickedListener(this);
mBtnSend.setClickedListener(this);
}
@Override
public void onClick(Component component) {
int viewId = component.getId();
if (viewId == ResourceTable.Id_scan) {
// 處理掃描
disposeScan();
} else if (viewId == ResourceTable.Id_connect) {
// 處理連接
disposeConnect();
} else if (viewId == ResourceTable.Id_send) {
// 向外圍設(shè)備發(fā)送消息
disposeSend();
}
}
/**
* 處理掃描
*/
private void disposeScan() {
if (!isScanning) {
isScanning = true;
mBtnScan.setText("停止掃描");
mTvDevice.setText("設(shè)備mac:正在掃描...");
mTvName.setText("設(shè)備名稱:暫無(wú)設(shè)備");
// 開(kāi)始掃描(過(guò)濾器指定的)設(shè)備
BleScanFilter bleScanFilter = new BleScanFilter();
bleScanFilter.setPeripheralAddress(Constant.PERIPHERAL_ADDRESS);
mFilters.add(bleScanFilter);
mCentralManager.startScan(mFilters);
} else {
isScanning = false;
mBtnScan.setText("開(kāi)始掃描");
// 停止掃描
mCentralManager.stopScan();
}
}
/**
* 處理連接
*/
private void disposeConnect() {
if (mPeripheralDevice == null) {
// 外圍設(shè)備對(duì)象未連接
mTvStatus.setText("連接狀態(tài):請(qǐng)先掃描獲取設(shè)備信息");
return;
}
if (!isConnected) {
mBtnConnect.setText("斷開(kāi)連接");
mTvStatus.setText("連接狀態(tài):連接中...");
// 發(fā)起連接
mPeripheralDevice.connect(false, mPeripheralCallback);
} else {
isConnected = false;
mBtnConnect.setText("連接設(shè)備");
mTvStatus.setText("連接狀態(tài):未連接");
mTvDevice.setText("設(shè)備mac:暫無(wú)設(shè)備");
mTvName.setText("設(shè)備名稱:暫無(wú)設(shè)備");
setBleImage();
// 發(fā)起斷開(kāi)連接
mPeripheralDevice.disconnect();
mPeripheralDevice = null;
}
}
/**
* 處理向外圍設(shè)備發(fā)送消息
*/
private void disposeSend() {
String msg = mTfInput.getText().toString();
if (msg.isEmpty() || mPeripheralDevice == null || !isConnected) {
return;
}
// 向外圍設(shè)備發(fā)送用戶輸入的數(shù)據(jù)
mWriteCharacteristic.setValue(msg.getBytes());
boolean result = mPeripheralDevice.writeCharacteristic(mWriteCharacteristic);
HiLog.info(label, "發(fā)送內(nèi)容:" + msg + "發(fā)送結(jié)果:" + result);
String sendResult = result ? "發(fā)送成功": "發(fā)送失敗";
showToast(sendResult);
}
/**
* 實(shí)現(xiàn)外圍設(shè)備操作回調(diào)
* 中心設(shè)備作為GattService的客戶端
*/
private class MyBlePeripheralCallback extends BlePeripheralCallback {
/**
* 在中心設(shè)備上發(fā)現(xiàn)服務(wù)(GattService外圍設(shè)備)的回調(diào)
* @param status 狀態(tài)
*/
@Override
public void servicesDiscoveredEvent(int status) { // 外圍設(shè)備服務(wù)發(fā)生更新觸發(fā)的回調(diào)。
if (status == BlePeripheralDevice.OPERATION_SUCC) {
HiLog.info(label, "servicesDiscoveredEvent OPERATION_SUCC");
List<GattService> services = mPeripheralDevice.getServices(); // 獲取Service成功后獲服務(wù)列表
for (GattService service : services) {
// 對(duì)每個(gè)服務(wù)進(jìn)行相應(yīng)操作
if (service.getUuid().equals(UUID.fromString(Constant.SERVICE_UUID))) {
HiLog.info(label, "servicesDiscoveredEvent 找到服務(wù)");
mPeripheralDevice.setNotifyCharacteristic(service.getCharacteristic(UUID.fromString(Constant.NOTIFY_CHARACTER_UUID)).get(), true);
}
}
}
}
/**
* 連接狀態(tài)變更
* 連接成功后可以在中心設(shè)備上(客戶端)發(fā)現(xiàn)GattService
* @param connectionState
*/
@Override
public void connectionStateChangeEvent(int connectionState) {
super.connectionStateChangeEvent(connectionState);
HiLog.info(label, "connectionState:" + connectionState);
if (connectionState == ProfileBase.STATE_CONNECTED && !isConnected) {
isConnected = true;
mPeripheralDevice.discoverServices();// 與外圍設(shè)備連接成功,發(fā)現(xiàn)GattService
setText(mTvStatus, "連接狀態(tài):已連接");
} else if (connectionState == ProfileBase.STATE_DISCONNECTED) {
// 斷開(kāi)連接
setText(mTvStatus, "連接狀態(tài):未連接");
}
}
/**
* 特性變更的回調(diào)
* 接受外圍設(shè)備發(fā)送的數(shù)據(jù)
* @param characteristic
*/
@Override
public void characteristicChangedEvent(GattCharacteristic characteristic) {
super.characteristicChangedEvent(characteristic);
// 更新外圍設(shè)備發(fā)送的數(shù)據(jù)
String msg = new String(characteristic.getValue());
HiLog.info(label, "characteristicChangedEvent msg=" + msg);
setText(mTvData, msg);
}
}
/**
* 實(shí)現(xiàn)中心設(shè)備管理回調(diào)
*/
public class MyBleCentralManagerCallback implements BleCentralManagerCallback{
/**
* 掃碼結(jié)果回調(diào)
* @param bleScanResult 掃碼結(jié)果
*/
@Override
public void scanResultEvent(BleScanResult bleScanResult) {
// 根據(jù)掃碼結(jié)果獲取外圍設(shè)備實(shí)例
if (mPeripheralDevice == null) {
String deviceAddr = bleScanResult.getPeripheralDevice().getDeviceAddr();
String deviceName = bleScanResult.getPeripheralDevice().getDeviceName().get();
HiLog.info(label, "設(shè)備mac:" + deviceAddr);
if (Constant.PERIPHERAL_ADDRESS.equals(deviceAddr)) {
mPeripheralDevice = bleScanResult.getPeripheralDevice();
setText(mTvDevice, "設(shè)備mac:" + deviceAddr);
setText(mTvName, "設(shè)備名稱:" + deviceName);
}
}
}
/**
* 掃描失敗回調(diào)
* @param i
*/
@Override
public void scanFailedEvent(int i) {
setText(mTvDevice, "設(shè)備mac:掃描失敗,請(qǐng)重新掃描");
setText(mTvName, "設(shè)備名稱:暫無(wú)設(shè)備");
}
/**
* 組掃描成功回調(diào)
* @param list 組信息
*/
@Override
public void groupScanResultsEvent(List<BleScanResult> list) {
}
}
/**
* 設(shè)置Text的內(nèi)容
* @param text 容器
* @param content 內(nèi)容
*/
private void setText(Text text, final String content) {
getUITaskDispatcher().syncDispatch(new Runnable() {
@Override
public void run() {
text.setText(content);
}
});
}
private void showToast(String msg) {
ToastDialog toastDialog = new ToastDialog(this);
toastDialog.setAlignment(LayoutAlignment.CENTER).setText(msg).show();
}
@Override
protected void onStop() {
super.onStop();
if (mPeripheralDevice != null) {
mPeripheralDevice.disconnect();
mPeripheralDevice = null;
}
}
private void setBleImage() {
if (isConnected) {
mIvBle.setPixelMap(ResourceTable.Media_icon_ble_ling);
} else {
mIvBle.setPixelMap(ResourceTable.Media_icon_ble_black);
}
}
}
??想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)??
??https://harmonyos.51cto.com??