系列文章《鸿蒙开发从入门到专家》第 25 篇 · 高级
应用开发做到一定深度,必然要碰硬件。鸿蒙的生态不止手机,还有耳机、手表、车机、智能家居。设备之间怎么通信?最常见的就是蓝牙低功耗(BLE)和 NFC。这两块技术细节多、坑也多,本文把核心链路串一遍。
一、BLE 开发整体流程
BLE(Bluetooth Low Energy)适合低功耗、小数据量的场景,比如手环同步、体温计读取、Beacon 定位。一个完整的 BLE 中心设备流程包括:
申请权限并打开蓝牙。
扫描周边从设备。
连接目标设备。
发现服务(Service)和特征(Characteristic)。
读写特征或订阅通知(Notify/Indicate)。
断开连接。
鸿蒙通过 @kit.ConnectivityKit 中的 bluetooth 模块提供 BLE API。
二、扫描与连接
import { bluetooth } from '@kit.ConnectivityKit';
// 开启蓝牙
bluetooth.enableBluetooth();
// 扫描设备
bluetooth.BLE.startBLEScan([
{
serviceUuid: '0000180d-0000-1000-8000-00805f9b34fb', // 心率服务
}
], {
interval: 0,
dutyMode: bluetooth.ScanDuty.SCAN_DUTY_LOW_POWER,
});
// 监听扫描结果
bluetooth.BLE.on('BLEDeviceFind', (data) => {
for (const device of data.data) {
console.info(`发现设备:${device.deviceName},RSSI:${device.rssi}`);
}
});
扫描到设备后,用 deviceId 发起连接:
const gattClient = bluetooth.BLE.createGattClientDevice(deviceId);
await gattClient.connect();
连接成功后就可以发现服务了。
三、GATT 通信:读、写、订阅
GATT 是 BLE 的通信协议。每个设备提供若干 Service,每个 Service 下包含若干 Characteristic。Characteristic 是实际读写的最小单元。
// 发现服务
const services = await gattClient.getServices();
for (const service of services) {
console.info(`Service UUID: ${service.serviceUuid}`);
for (const char of service.characteristics) {
console.info(` Characteristic UUID: ${char.characteristicUuid}`);
}
}
// 读取特征值
const value = await gattClient.readCharacteristicValue({
serviceUuid: '0000180d-0000-1000-8000-00805f9b34fb',
characteristicUuid: '00002a37-0000-1000-8000-00805f9b34fb',
});
// 订阅特征通知
await gattClient.setCharacteristicChangeNotification({
serviceUuid: '0000180d-0000-1000-8000-00805f9b34fb',
characteristicUuid: '00002a37-0000-1000-8000-00805f9b34fb',
enable: true,
});
gattClient.on('BLECharacteristicChange', (data) => {
const heartRate = parseHeartRate(data.data);
console.info(`心率:${heartRate}`);
});
解析心率特征时要注意:第一个字节的最低位是心率格式标志。0 表示 UINT8,1 表示 UINT16。这种细节直接影响读数正确性。
四、NFC 标签读写
NFC 适合近距离、一次性交互,比如刷门禁、读商品信息、碰一碰配网。鸿蒙通过 @kit.ConnectivityKit 的 nfc 模块或 tag 相关 API 操作标签。
NFC 开发有两个关键点:
前台分发:应用在前台时优先处理发现的 NFC 标签。
标签类型:常见的有 NDEF、MifareClassic、ISO-DEP 等,不同类型读写方式不同。
读 NDEF 标签的大致流程:
import { tag } from '@kit.ConnectivityKit';
// 在 Ability 的 onCreate 或页面 onReady 中注册前台分发
nfc.getNfcController().on('tagDiscovered', (info) => {
const ndef = tag.getNdef(info.tagInfo);
if (ndef) {
ndef.connect();
const message = ndef.getNdefMessage();
for (const record of message.records) {
console.info(`NDEF 记录:${record.payload}`);
}
ndef.close();
}
});
写标签时把文本或 URI 封装成 NDEF 记录:
const record = tag.NdefRecord.createTextRecord('Hello HarmonyOS');
const msg = tag.NdefMessage.createNdefMessage([record]);
ndef.connect();
ndef.writeNdefMessage(msg);
ndef.close();
五、权限与用户体验
蓝牙和 NFC 都属于敏感权限。module.json5 中需要声明:
{
"name": "ohos.permission.ACCESS_BLUETOOTH",
"reason": "$string:bluetooth_reason"
},
{
"name": "ohos.permission.DISCOVER_BLUETOOTH",
"reason": "$string:bluetooth_reason"
},
{
"name": "ohos.permission.NFC",
"reason": "$string:nfc_reason"
}
蓝牙扫描会消耗电量,建议扫描到目标设备后立即停止扫描。另外,Android 和 iOS 都有后台扫描限制,鸿蒙也不例外,后台 BLE 扫描需要申请特殊权限或走后台任务机制。
六、实战:蓝牙温度计数据同步
以一个 BLE 温度计为例:
扫描时按服务 UUID 过滤,只保留温度计设备。
连接后读取电池服务(0x180F)确认电量。
订阅温度测量特征(0x2A1C),实时拿到温度值。
数据解析后写入本地 RDB,并刷新 UI。
用户退出页面时断开连接并释放 GattClient。
小结
BLE 和 NFC 都是近场通信,但适用场景不同:
BLE:持续连接、数据流、双向交互。
NFC:一次性触碰、快速配对、标签读写。
两者都需要仔细处理权限、生命周期和异常断开。协议细节(GATT 特征格式、NDEF 记录类型)是调试时最容易卡住的地方。
下一篇我们聊聊传感器:加速度计、陀螺仪和传感器融合。