蓝牙GATT:HarmonyOS APP开发服务与特征值操作
【摘要】 蓝牙GATT:HarmonyOS APP开发服务与特征值操作上一篇我们聊了BLE的扫描与连接,但连接只是第一步——真正有价值的是连接后的数据交互。这就涉及到GATT(Generic Attribute Profile)了,它是BLE数据交换的核心协议。今天咱们就深入聊聊GATT的服务与特征值操作,看看如何优雅地读写数据、订阅通知,实现设备与手机的"对话"。 一、背景与动机 1.1 GATT...
蓝牙GATT:HarmonyOS APP开发服务与特征值操作
上一篇我们聊了BLE的扫描与连接,但连接只是第一步——真正有价值的是连接后的数据交互。这就涉及到GATT(Generic Attribute Profile)了,它是BLE数据交换的核心协议。今天咱们就深入聊聊GATT的服务与特征值操作,看看如何优雅地读写数据、订阅通知,实现设备与手机的"对话"。
一、背景与动机
1.1 GATT架构详解
GATT定义了BLE设备间数据交换的层次结构,理解这个架构是BLE开发的基础:

核心概念解释:
| 概念 | 说明 | 类比 |
|---|---|---|
| Profile | 规范定义,如心率Profile | 一类应用的标准 |
| Service | 服务,一组相关数据 | 一个功能模块 |
| Characteristic | 特征值,具体数据项 | 一个数据变量 |
| Descriptor | 描述符,描述特征值属性 | 变量的属性 |
| UUID | 唯一标识符 | 变量的地址 |
1.2 标准UUID与自定义UUID
BLE定义了一系列标准UUID,用于常见应用:
// 常用标准UUID
const STANDARD_UUIDS = {
// 通用访问服务
GAP_SERVICE: '00001800-0000-1000-8000-00805F9B34FB',
DEVICE_NAME: '00002A00-0000-1000-8000-00805F9B34FB',
// 通用属性服务
GATT_SERVICE: '00001801-0000-1000-8000-00805F9B34FB',
// 心率服务
HEART_RATE_SERVICE: '0000180D-0000-1000-8000-00805F9B34FB',
HEART_RATE_MEASUREMENT: '00002A37-0000-1000-8000-00805F9B34FB',
// 血压服务
BLOOD_PRESSURE_SERVICE: '00001813-0000-1000-8000-00805F9B34FB',
// 电池服务
BATTERY_SERVICE: '0000180F-0000-1000-8000-00805F9B34FB',
BATTERY_LEVEL: '00002A19-0000-1000-8000-00805F9B34FB',
// 设备信息服务
DEVICE_INFO_SERVICE: '0000180A-0000-1000-8000-00805F9B34FB',
MANUFACTURER_NAME: '00002A29-0000-1000-8000-00805F9B34FB',
MODEL_NUMBER: '00002A24-0000-1000-8000-00805F9B34FB',
FIRMWARE_REVISION: '00002A26-0000-1000-8000-00805F9B34FB'
};
// 自定义UUID示例(厂商自定义)
const CUSTOM_UUIDS = {
// 智能灯服务
LIGHT_SERVICE: '0000FFF0-0000-1000-8000-00805F9B34FB',
LIGHT_SWITCH: '0000FFF1-0000-1000-8000-00805F9B34FB',
LIGHT_BRIGHTNESS: '0000FFF2-0000-1000-8000-00805F9B34FB',
LIGHT_COLOR: '0000FFF3-0000-1000-8000-00805F9B34FB',
// 温湿度传感器服务
TH_SENSOR_SERVICE: '0000FFE0-0000-1000-8000-00805F9B34FB',
TEMPERATURE: '0000FFE1-0000-1000-8000-00805F9B34FB',
HUMIDITY: '0000FFE2-0000-1000-8000-00805F9B34FB'
};
二、核心原理
2.1 特征值属性
每个特征值都有一组属性,决定了它可以被如何操作:
// 特征值属性
const CHARACTERISTIC_PROPERTIES = {
// 可读
READ: 0x02,
// 可写(无需响应)
WRITE_NO_RESPONSE: 0x04,
// 可写(需要响应)
WRITE: 0x08,
// 可通知
NOTIFY: 0x10,
// 可指示(需要确认)
INDICATE: 0x20,
// 广播
BROADCAST: 0x01,
// 扩展属性
EXTENDED_PROPERTIES: 0x80
};
/**
* 检查特征值是否支持某属性
*/
function hasProperty(properties: number, flag: number): boolean {
return (properties & flag) !== 0;
}
// 使用示例
const charProperties = 0x1A; // 通知 + 写 + 读
console.info(`可读: ${hasProperty(charProperties, CHARACTERISTIC_PROPERTIES.READ)}`);
console.info(`可通知: ${hasProperty(charProperties, CHARACTERISTIC_PROPERTIES.NOTIFY)}`);
2.2 数据操作流程

2.3 CCCD(Client Characteristic Configuration Descriptor)
CCCD是开启/关闭通知的关键描述符:
// CCCD UUID
const CCCD_UUID = '00002902-0000-1000-8000-00805F9B34FB';
// CCCD值
const CCCD_VALUES = {
DISABLE: 0x0000, // 禁用
NOTIFICATION: 0x0001, // 启用通知
INDICATION: 0x0002 // 启用指示
};
三、代码实战
3.1 GATT服务发现与解析
import bluetooth from '@ohos.bluetooth';
import { BleManager } from './BleManager';
/**
* GATT服务管理器
* 封装服务发现、特征值操作等功能
*/
export class GattServiceManager {
private bleManager: BleManager = BleManager.getInstance();
private gattClient: bluetooth.GattClient | null = null;
// 服务缓存
private services: Map<string, bluetooth.GattService> = new Map();
// 特征值缓存
private characteristics: Map<string, bluetooth.GattCharacteristic> = new Map();
/**
* 初始化GATT服务管理
* 连接成功后调用
*/
public async init(): Promise<boolean> {
try {
// 发现所有服务
const serviceList = await this.discoverAllServices();
if (!serviceList) {
return false;
}
// 缓存服务和特征值
serviceList.forEach((service) => {
this.services.set(service.serviceUuid, service);
service.characteristics.forEach((char) => {
const key = `${service.serviceUuid}:${char.characteristicUuid}`;
this.characteristics.set(key, char);
});
});
console.info(`[GATT] 发现 ${this.services.size} 个服务`);
console.info(`[GATT] 发现 ${this.characteristics.size} 个特征值`);
return true;
} catch (error) {
console.error('[GATT] 初始化失败:', error);
return false;
}
}
/**
* 发现所有GATT服务
*/
private async discoverAllServices(): Promise<Array<bluetooth.GattService> | null> {
const services = await this.bleManager.discoverServices();
return services;
}
/**
* 获取所有服务
*/
public getAllServices(): Array<bluetooth.GattService> {
return Array.from(this.services.values());
}
/**
* 获取指定服务
*/
public getService(serviceUuid: string): bluetooth.GattService | undefined {
return this.services.get(serviceUuid);
}
/**
* 获取服务的所有特征值
*/
public getCharacteristics(serviceUuid: string): Array<bluetooth.GattCharacteristic> {
const service = this.services.get(serviceUuid);
return service ? service.characteristics : [];
}
/**
* 获取指定特征值
*/
public getCharacteristic(
serviceUuid: string,
characteristicUuid: string
): bluetooth.GattCharacteristic | undefined {
const key = `${serviceUuid}:${characteristicUuid}`;
return this.characteristics.get(key);
}
/**
* 打印服务结构(调试用)
*/
public printServiceStructure(): void {
console.info('=== GATT服务结构 ===');
this.services.forEach((service, serviceUuid) => {
console.info(`服务: ${serviceUuid}`);
service.characteristics.forEach((char) => {
const props = this.parseProperties(char.properties);
console.info(` 特征值: ${char.characteristicUuid}`);
console.info(` 属性: ${props.join(', ')}`);
if (char.descriptors) {
char.descriptors.forEach((desc) => {
console.info(` 描述符: ${desc.descriptorUuid}`);
});
}
});
});
}
/**
* 解析特征值属性
*/
private parseProperties(properties: number): string[] {
const props: string[] = [];
if (properties & 0x01) props.push('Broadcast');
if (properties & 0x02) props.push('Read');
if (properties & 0x04) props.push('WriteNoResponse');
if (properties & 0x08) props.push('Write');
if (properties & 0x10) props.push('Notify');
if (properties & 0x20) props.push('Indicate');
if (properties & 0x80) props.push('ExtendedProperties');
return props;
}
/**
* 检查特征值是否支持读
*/
public isReadable(char: bluetooth.GattCharacteristic): boolean {
return (char.properties & 0x02) !== 0;
}
/**
* 检查特征值是否支持写
*/
public isWritable(char: bluetooth.GattCharacteristic): boolean {
return (char.properties & 0x08) !== 0;
}
/**
* 检查特征值是否支持通知
*/
public isNotifiable(char: bluetooth.GattCharacteristic): boolean {
return (char.properties & 0x10) !== 0;
}
/**
* 清空缓存
*/
public clear(): void {
this.services.clear();
this.characteristics.clear();
}
}
3.2 特征值读写操作封装
import bluetooth from '@ohos.bluetooth';
import { BleManager } from './BleManager';
/**
* GATT数据操作器
* 封装特征值的读写、通知等操作
*/
export class GattDataOperator {
private bleManager: BleManager = BleManager.getInstance();
// 通知回调映射
private notificationCallbacks: Map<string, (data: ArrayBuffer) => void> = new Map();
/**
* 读取特征值(返回原始数据)
*/
public async readRaw(
serviceUuid: string,
characteristicUuid: string
): Promise<ArrayBuffer | null> {
return await this.bleManager.readCharacteristic(serviceUuid, characteristicUuid);
}
/**
* 读取特征值(返回字符串)
*/
public async readString(
serviceUuid: string,
characteristicUuid: string
): Promise<string | null> {
const data = await this.readRaw(serviceUuid, characteristicUuid);
if (!data) return null;
return this.arrayBufferToString(data);
}
/**
* 读取特征值(返回数字)
*/
public async readNumber(
serviceUuid: string,
characteristicUuid: string,
bitLength: 8 | 16 | 32 = 8,
signed: boolean = false
): Promise<number | null> {
const data = await this.readRaw(serviceUuid, characteristicUuid);
if (!data) return null;
return this.arrayBufferToNumber(data, bitLength, signed);
}
/**
* 写入字符串
*/
public async writeString(
serviceUuid: string,
characteristicUuid: string,
value: string
): Promise<boolean> {
const data = this.stringToArrayBuffer(value);
return await this.bleManager.writeCharacteristic(
serviceUuid,
characteristicUuid,
data
);
}
/**
* 写入数字
*/
public async writeNumber(
serviceUuid: string,
characteristicUuid: string,
value: number,
bitLength: 8 | 16 | 32 = 8
): Promise<boolean> {
const data = this.numberToArrayBuffer(value, bitLength);
return await this.bleManager.writeCharacteristic(
serviceUuid,
characteristicUuid,
data
);
}
/**
* 写入字节数组
*/
public async writeBytes(
serviceUuid: string,
characteristicUuid: string,
bytes: number[]
): Promise<boolean> {
const data = new ArrayBuffer(bytes.length);
const view = new Uint8Array(data);
for (let i = 0; i < bytes.length; i++) {
view[i] = bytes[i];
}
return await this.bleManager.writeCharacteristic(
serviceUuid,
characteristicUuid,
data
);
}
/**
* 启用通知
*/
public async enableNotification(
serviceUuid: string,
characteristicUuid: string,
callback: (data: ArrayBuffer) => void
): Promise<boolean> {
const key = `${serviceUuid}:${characteristicUuid}`;
// 保存回调
this.notificationCallbacks.set(key, callback);
// 设置全局回调
this.bleManager.setOnCharacteristicChanged((data: ArrayBuffer) => {
// 这里简化处理,实际需要根据特征值UUID分发
callback(data);
});
return await this.bleManager.enableNotification(
serviceUuid,
characteristicUuid,
true
);
}
/**
* 禁用通知
*/
public async disableNotification(
serviceUuid: string,
characteristicUuid: string
): Promise<boolean> {
const key = `${serviceUuid}:${characteristicUuid}`;
this.notificationCallbacks.delete(key);
return await this.bleManager.enableNotification(
serviceUuid,
characteristicUuid,
false
);
}
/**
* 数据转换工具方法
*/
private arrayBufferToString(data: ArrayBuffer): string {
const view = new Uint8Array(data);
let result = '';
for (let i = 0; i < view.length; i++) {
result += String.fromCharCode(view[i]);
}
return result;
}
private stringToArrayBuffer(str: string): ArrayBuffer {
const buffer = new ArrayBuffer(str.length);
const view = new Uint8Array(buffer);
for (let i = 0; i < str.length; i++) {
view[i] = str.charCodeAt(i);
}
return buffer;
}
private arrayBufferToNumber(
data: ArrayBuffer,
bitLength: number,
signed: boolean
): number {
const view = new DataView(data);
switch (bitLength) {
case 8:
return signed ? view.getInt8(0) : view.getUint8(0);
case 16:
return signed ? view.getInt16(0, true) : view.getUint16(0, true);
case 32:
return signed ? view.getInt32(0, true) : view.getUint32(0, true);
default:
return view.getUint8(0);
}
}
private numberToArrayBuffer(value: number, bitLength: number): ArrayBuffer {
const buffer = new ArrayBuffer(bitLength / 8);
const view = new DataView(buffer);
switch (bitLength) {
case 8:
view.setUint8(0, value);
break;
case 16:
view.setUint16(0, value, true);
break;
case 32:
view.setUint32(0, value, true);
break;
}
return buffer;
}
}
3.3 完整的设备控制示例
import { GattServiceManager } from './GattServiceManager';
import { GattDataOperator } from './GattDataOperator';
/**
* 智能手环控制器
* 综合示例:心率、步数、电池等数据读取
*/
export class SmartBandController {
private serviceManager: GattServiceManager = new GattServiceManager();
private dataOperator: GattDataOperator = new GattDataOperator();
// 标准服务UUID
private readonly HEART_RATE_SERVICE = '0000180D-0000-1000-8000-00805F9B34FB';
private readonly HEART_RATE_MEASUREMENT = '00002A37-0000-1000-8000-00805F9B34FB';
private readonly BATTERY_SERVICE = '0000180F-0000-1000-8000-00805F9B34FB';
private readonly BATTERY_LEVEL = '00002A19-0000-1000-8000-00805F9B34FB';
private readonly DEVICE_INFO_SERVICE = '0000180A-0000-1000-8000-00805F9B34FB';
private readonly MANUFACTURER_NAME = '00002A29-0000-1000-8000-00805F9B34FB';
private readonly MODEL_NUMBER = '00002A24-0000-1000-8000-00805F9B34FB';
private readonly FIRMWARE_REVISION = '00002A26-0000-1000-8000-00805F9B34FB';
// 回调
private onHeartRateChanged: ((rate: number) => void) | null = null;
private onBatteryChanged: ((level: number) => void) | null = null;
/**
* 初始化
*/
public async init(): Promise<boolean> {
return await this.serviceManager.init();
}
/**
* 获取设备信息
*/
public async getDeviceInfo(): Promise<{
manufacturer: string;
model: string;
firmware: string;
}> {
const manufacturer = await this.dataOperator.readString(
this.DEVICE_INFO_SERVICE,
this.MANUFACTURER_NAME
);
const model = await this.dataOperator.readString(
this.DEVICE_INFO_SERVICE,
this.MODEL_NUMBER
);
const firmware = await this.dataOperator.readString(
this.DEVICE_INFO_SERVICE,
this.FIRMWARE_REVISION
);
return {
manufacturer: manufacturer || 'Unknown',
model: model || 'Unknown',
firmware: firmware || 'Unknown'
};
}
/**
* 读取电池电量
*/
public async getBatteryLevel(): Promise<number> {
const level = await this.dataOperator.readNumber(
this.BATTERY_SERVICE,
this.BATTERY_LEVEL,
8
);
return level || 0;
}
/**
* 开始心率监测
*/
public async startHeartRateMonitor(): Promise<boolean> {
return await this.dataOperator.enableNotification(
this.HEART_RATE_SERVICE,
this.HEART_RATE_MEASUREMENT,
(data: ArrayBuffer) => {
const heartRate = this.parseHeartRateData(data);
if (this.onHeartRateChanged) {
this.onHeartRateChanged(heartRate);
}
}
);
}
/**
* 停止心率监测
*/
public async stopHeartRateMonitor(): Promise<boolean> {
return await this.dataOperator.disableNotification(
this.HEART_RATE_SERVICE,
this.HEART_RATE_MEASUREMENT
);
}
/**
* 解析心率数据
* 根据蓝牙心率服务规范
*/
private parseHeartRateData(data: ArrayBuffer): number {
const view = new DataView(data);
const flags = view.getUint8(0);
// 检查心率值格式
const is16Bit = (flags & 0x01) === 1;
if (is16Bit) {
return view.getUint16(1, true);
} else {
return view.getUint8(1);
}
}
/**
* 设置回调
*/
public setOnHeartRateChanged(callback: (rate: number) => void): void {
this.onHeartRateChanged = callback;
}
public setOnBatteryChanged(callback: (level: number) => void): void {
this.onBatteryChanged = callback;
}
}
/**
* 智能家居控制器示例
*/
export class SmartHomeController {
private serviceManager: GattServiceManager = new GattServiceManager();
private dataOperator: GattDataOperator = new GattDataOperator();
// 自定义服务UUID
private readonly HOME_SERVICE = '0000FFF0-0000-1000-8000-00805F9B34FB';
private readonly SWITCH_CHAR = '0000FFF1-0000-1000-8000-00805F9B34FB';
private readonly BRIGHTNESS_CHAR = '0000FFF2-0000-1000-8000-00805F9B34FB';
private readonly COLOR_CHAR = '0000FFF3-0000-1000-8000-00805F9B34FB';
private readonly TEMPERATURE_CHAR = '0000FFF4-0000-1000-8000-00805F9B34FB';
/**
* 初始化
*/
public async init(): Promise<boolean> {
return await this.serviceManager.init();
}
/**
* 开灯
*/
public async turnOnLight(): Promise<boolean> {
return await this.dataOperator.writeBytes(
this.HOME_SERVICE,
this.SWITCH_CHAR,
[0x01]
);
}
/**
* 关灯
*/
public async turnOffLight(): Promise<boolean> {
return await this.dataOperator.writeBytes(
this.HOME_SERVICE,
this.SWITCH_CHAR,
[0x00]
);
}
/**
* 设置亮度
* @param brightness 亮度值 0-100
*/
public async setBrightness(brightness: number): Promise<boolean> {
const value = Math.max(0, Math.min(100, brightness));
return await this.dataOperator.writeBytes(
this.HOME_SERVICE,
this.BRIGHTNESS_CHAR,
[value]
);
}
/**
* 设置颜色(RGB)
*/
public async setColor(r: number, g: number, b: number): Promise<boolean> {
return await this.dataOperator.writeBytes(
this.HOME_SERVICE,
this.COLOR_CHAR,
[
Math.max(0, Math.min(255, r)),
Math.max(0, Math.min(255, g)),
Math.max(0, Math.min(255, b))
]
);
}
/**
* 读取温度
*/
public async getTemperature(): Promise<number> {
// 假设温度是16位有符号整数,单位0.01℃
const rawValue = await this.dataOperator.readNumber(
this.HOME_SERVICE,
this.TEMPERATURE_CHAR,
16,
true
);
if (rawValue === null) return 0;
return rawValue / 100; // 转换为摄氏度
}
}
四、踩坑与注意事项
4.1 UUID格式问题
坑点:UUID格式不统一,需要规范化处理。
/**
* UUID规范化
*/
function normalizeUuid(uuid: string): string {
// 移除所有连字符
let normalized = uuid.replace(/-/g, '').toUpperCase();
// 补全128位UUID
if (normalized.length === 4) {
// 16位UUID -> 128位
normalized = `0000${normalized}00001000800000805F9B34FB`;
} else if (normalized.length === 8) {
// 32位UUID -> 128位
normalized = `${normalized}00001000800000805F9B34FB`;
}
// 添加连字符
return [
normalized.substring(0, 8),
normalized.substring(8, 12),
normalized.substring(12, 16),
normalized.substring(16, 20),
normalized.substring(20, 32)
].join('-');
}
// 使用示例
const shortUuid = '180D'; // 心率服务
const fullUuid = normalizeUuid(shortUuid);
console.info(fullUuid); // 0000180D-0000-1000-8000-00805F9B34FB
4.2 操作队列管理
坑点:GATT操作不能并发执行,需要排队。
/**
* GATT操作队列
* 确保操作顺序执行
*/
export class GattOperationQueue {
private queue: Array<() => Promise<any>> = [];
private isProcessing: boolean = false;
/**
* 添加操作到队列
*/
public async enqueue<T>(operation: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await operation();
resolve(result);
} catch (error) {
reject(error);
}
});
this.processQueue();
});
}
/**
* 处理队列
*/
private async processQueue(): Promise<void> {
if (this.isProcessing || this.queue.length === 0) {
return;
}
this.isProcessing = true;
while (this.queue.length > 0) {
const operation = this.queue.shift();
if (operation) {
await operation();
}
}
this.isProcessing = false;
}
/**
* 清空队列
*/
public clear(): void {
this.queue = [];
}
}
// 使用示例
const operationQueue = new GattOperationQueue();
// 所有GATT操作都通过队列执行
async function safeRead(serviceUuid: string, charUuid: string): Promise<ArrayBuffer | null> {
return await operationQueue.enqueue(() => {
return bleManager.readCharacteristic(serviceUuid, charUuid);
});
}
4.3 数据解析错误处理
坑点:设备返回的数据格式可能不符合预期。
/**
* 安全的数据解析
*/
function safeParseHeartRate(data: ArrayBuffer): number | null {
try {
if (data.byteLength < 2) {
console.error('[心率] 数据长度不足');
return null;
}
const view = new DataView(data);
const flags = view.getUint8(0);
const is16Bit = (flags & 0x01) === 1;
// 检查数据长度是否足够
if (is16Bit && data.byteLength < 3) {
console.error('[心率] 16位心率数据长度不足');
return null;
}
const heartRate = is16Bit ? view.getUint16(1, true) : view.getUint8(1);
// 合理性检查
if (heartRate < 30 || heartRate > 220) {
console.warn(`[心率] 数值异常: ${heartRate}`);
return null;
}
return heartRate;
} catch (error) {
console.error('[心率] 解析失败:', error);
return null;
}
}
4.4 连接状态检查
坑点:操作前需要确认连接状态。
/**
* 安全的GATT操作包装
*/
async function safeGattOperation<T>(
operation: () => Promise<T>,
deviceCheck: () => boolean
): Promise<T | null> {
// 检查连接状态
if (!deviceCheck()) {
console.error('[GATT] 设备未连接');
return null;
}
try {
return await operation();
} catch (error) {
const err = error as BusinessError;
// 处理特定错误
if (err.code === 133) {
console.error('[GATT] GATT操作失败,可能连接已断开');
// 触发重连逻辑
}
return null;
}
}
4.5 描述符操作
坑点:有时需要直接操作描述符。
/**
* 读取描述符
*/
async function readDescriptor(
serviceUuid: string,
characteristicUuid: string,
descriptorUuid: string
): Promise<ArrayBuffer | null> {
if (!gattClient) return null;
try {
const value = await gattClient.readDescriptorValue({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid,
descriptorUuid: descriptorUuid
});
return value;
} catch (error) {
console.error('[GATT] 读取描述符失败:', error);
return null;
}
}
/**
* 写入描述符
* 用于手动控制CCCD
*/
async function writeCccd(
serviceUuid: string,
characteristicUuid: string,
enable: boolean
): Promise<boolean> {
const CCCD_UUID = '00002902-0000-1000-8000-00805F9B34FB';
const data = new ArrayBuffer(2);
new DataView(data).setUint16(0, enable ? 0x0001 : 0x0000, true);
if (!gattClient) return false;
try {
await gattClient.writeDescriptorValue({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid,
descriptorUuid: CCCD_UUID,
descriptorValue: data
});
return true;
} catch (error) {
console.error('[GATT] 写入CCCD失败:', error);
return false;
}
}
五、HarmonyOS 6适配
5.1 API变更
| API | HarmonyOS 5 | HarmonyOS 6 | 说明 |
|---|---|---|---|
| discoverServices() | 返回服务列表 | 返回增强的服务对象 | 包含更多属性 |
| readCharacteristicValue() | 基础功能 | 支持超时设置 | 新增timeout参数 |
| 新增 | - | getCharacteristicProperties() | 独立获取属性 |
适配代码:
/**
* HarmonyOS 6 GATT适配
*/
async function readCharacteristicAdaptive(
serviceUuid: string,
characteristicUuid: string,
timeout?: number
): Promise<ArrayBuffer | null> {
const apiVersion = getApiVersion();
try {
if (apiVersion >= 6 && timeout) {
// HarmonyOS 6 支持超时
return await gattClient.readCharacteristicValue({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid
}, { timeout: timeout });
} else {
// HarmonyOS 5
return await gattClient.readCharacteristicValue({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid
});
}
} catch (error) {
console.error('[GATT] 读取失败:', error);
return null;
}
}
5.2 新增功能
HarmonyOS 6增强了GATT能力:
// 1. 批量读取
const multipleValues = await gattClient.readMultipleCharacteristicValues([
{ serviceUuid: service1, characteristicUuid: char1 },
{ serviceUuid: service2, characteristicUuid: char2 }
]);
// 2. 可靠写入(Reliable Write)
const reliableWrite = gattClient.beginReliableWrite();
await reliableWrite.writeCharacteristicValue({ ... });
await reliableWrite.writeCharacteristicValue({ ... });
await reliableWrite.execute(); // 原子提交
// 3. 长读/写支持
const longValue = await gattClient.readLongCharacteristicValue({
serviceUuid: serviceUuid,
characteristicUuid: characteristicUuid,
offset: 0,
maxLength: 512
});
// 4. 服务变更通知
gattClient.on('serviceChange', () => {
console.info('[GATT] 服务已变更,重新发现');
discoverServices();
});
5.3 性能优化
HarmonyOS 6提供了更多性能调优选项:
// 1. 连接参数更新
await gattClient.requestConnectionPriority(
bluetooth.ConnectionPriority.CONNECTION_PRIORITY_HIGH
);
// 2. 数据长度扩展
await gattClient.requestDataLength(251); // 请求251字节数据长度
// 3. PHY设置
await gattClient.requestPreferredPhy({
txPhy: bluetooth.BlePhy.PHY_2M,
rxPhy: bluetooth.BlePhy.PHY_2M,
phyOptions: bluetooth.PhyOption.PHY_OPTION_NO_PREFERRED
});
六、总结
GATT是BLE数据交换的核心,掌握服务与特征值操作是BLE开发的关键技能。本文全面讲解了GATT的核心要点:
核心要点回顾:
- GATT架构:理解服务、特征值、描述符的层次关系
- UUID管理:区分标准UUID和自定义UUID,做好格式规范化
- 属性检查:操作前检查特征值是否支持对应属性
- 数据转换:正确处理ArrayBuffer与各种数据类型的转换
- 操作队列:避免并发操作,确保顺序执行
最佳实践建议:
- 封装GATT服务管理类,缓存服务和特征值信息
- 实现操作队列,避免并发问题
- 做好数据解析的错误处理和合理性检查
- 使用标准UUID提高兼容性
下一步学习:
- 蓝牙经典蓝牙RFCOMM通信(下一篇文章)
- BLE广播数据解析
- BLE安全机制
- 多设备同时连接管理
GATT看似复杂,其实就是一套"发现-读写-通知"的机制。希望本文能帮助你掌握GATT操作的核心技能,在BLE开发中游刃有余!
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)