HarmonyOS鸿蒙Next中如何实现蓝牙BLE设备的稳定连接与数据收发?

HarmonyOS鸿蒙Next中如何实现蓝牙BLE设备的稳定连接与数据收发? 我们开发一款健康手环配套 App,需要长时间连接 BLE 设备并接收心率数据。鸿蒙的蓝牙 API 是否支持后台持续通信?连接容易断开怎么办?

5 回复

开发者你好

  1. 蓝牙应用需要长期在后台运行可以申请长时任务保活蓝牙相关业务。
  2. 连接断开可以通过监听BLE蓝牙的连接状态判断。

【背景知识】

  • 蓝牙技术是一种无线通信技术,可以在短距离内传输数据,目前蓝牙有两种常见的技术分类:传统蓝牙(BR/EDR)和低功耗蓝牙(BLE)。两种类型的蓝牙区分可以参考:蓝牙服务开发概述
  • 在BLE蓝牙中,可以通过ScanFilter过滤参数精确找到需要连接的BLE蓝牙设备。
  • 经典蓝牙扫描无法配置过滤参数,通常只能通过扫描到的虚拟MAC地址,调用getRemoteDeviceName接口获取设备名称来确定目标设备。
  • 若对端设备同时支持经典蓝牙和BLE蓝牙,通过BLE蓝牙虚拟MAC地址建立蓝牙配对的同时,也会完成经典蓝牙的配对。

【解决方案】 开发者你好,蓝牙开发可以参考以下方案:

在module.json5申请相关权限,ohos.permission.ACCESS_BLUETOOTH允许应用接入蓝牙并使用蓝牙能力,例如配对、连接外围设备等,ohos.permission.USE_BLUETOOTH允许应用查看蓝牙的配置。关于蓝牙开发的完整示例可参考蓝牙开发

  1. 蓝牙应用需要长期在后台运行可以申请长时任务保活蓝牙相关业务。
  2. 连接断开可以通过监听BLE蓝牙的连接状态判断:
名称 说明
STATE_DISCONNECTED 0 表示profile已断连
STATE_CONNECTING 1 表示profile正在连接
STATE_CONNECTED 2 表示profile已连接
STATE_DISCONNECTING 3 表示profile正在断连

BLE蓝牙快速回连可以参考以下方式:

  • 方案一:与对端设备第一次连接后,发起蓝牙配对,配对完成后,蓝牙虚拟MAC地址会被固化。后续无需重复建立蓝牙扫描,调用getPairedDevices接口就可以从已配对列表中获取对端蓝牙的虚拟MAC地址,向对端设备发起连接。
  • 方案二:与对端设备第一次连接后,使用addPersistentDeviceId接口固化对端设备虚拟MAC地址。后续同样无需重复建立蓝牙扫描,调用getPersistentDeviceIds接口就可以从已固化设备列表中获取对端蓝牙的虚拟MAC地址,向对端设备发起连接。

示例demo如下:

import { access, ble, connection, constant } from '@kit.ConnectivityKit';

@Entry
@Component
struct Reconnect {
  @State gattClient: ble.GattClientDevice | undefined = undefined;

  // 方案一
  ReconnectOne() {
    // 先查询已配对设备列表,判断需要连接的设备是否在已配对设备列表中存在。
    let devices = connection.getPairedDevices();
    for (let index = 0; index < devices.length; index++) {
      let name = connection.getRemoteDeviceName(devices[index]);
      // 需要连接的设备在已配对设备列表中存在,无需发起扫描,直接创建实例进行连接。
      if (name === 'name') {
        // 创建ble蓝牙client实例
        this.gattClient = ble.createGattClientDevice(devices[index]);
        // 连接前先创建连接状态回调监听
        this.onBLEConnectionStateChangeOne();
        // 连接ble蓝牙
        this.gattClient.connect();
        return;
      }
    }
    // 需要连接的设备在已配对设备列表中不存在,开启常规ble蓝牙连接流程。
    // 订阅BLE设备发现
    this.onBLEDeviceFindOne();
    // 过滤参数可根据实际场景进行设置
    let scanFilter: ble.ScanFilter = {
      name: 'name'
    };
    // 扫描参数可根据实际场景配置
    let scanOptions: ble.ScanOptions = {
      interval: 500,
      dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
      matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE,
    };
    ble.startBLEScan([scanFilter], scanOptions);
  }

  onBLEDeviceFindOne() {
    ble.on('BLEDeviceFind', (data: Array<ble.ScanResult>) => {
      // 发现设备,创建实例进行连接。
      this.gattClient = ble.createGattClientDevice(data[0].deviceId);
      // 连接前先创建连接状态回调监听
      this.onBLEConnectionStateChangeOne();
      // 连接ble蓝牙
      this.gattClient.connect();
      // 连接成后,将已连接设备加入配对列表。
      // 注意:加入配对列表时机并不固定,可根据实际场景来进行变更。
      connection.pairDevice(data[0].deviceId, () => {
        console.info('pairDevice err');
      });
    });
  }

  onBLEConnectionStateChangeOne() {
    this.gattClient?.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => {
      if (state.state === constant.ProfileConnectionState.STATE_DISCONNECTED) {
        // 如果蓝牙意外断开了连接,可以在此处重新发起连接,以达到意外断连快速回连能力。
        // 不过需要保证此时this.gattClient实例没有被销毁。
        this.gattClient?.connect();
      }
    });
  }

  // 方案二
  ReconnectTwo() {
    // 先查询已固化设备列表,判断需要连接的设备是否在已固化设备列表中存在。
    let devices = access.getPersistentDeviceIds();
    for (let index = 0; index < devices.length; index++) {
      let name = connection.getRemoteDeviceName(devices[index]);
      let isValid = access.isValidRandomDeviceId(devices[index]);
      // 需要连接的设备在已固化设备列表中存在,同时该地址有效,无需发起扫描,直接创建实例进行连接。
      if (isValid && name === 'name') {
        // 创建ble蓝牙client实例
        this.gattClient = ble.createGattClientDevice(devices[index]);
        // 连接前先创建连接状态回调监听
        this.onBLEConnectionStateChangeTwo();
        // 连接ble蓝牙
        this.gattClient.connect();
        return;
      }
    }
    // 需要连接的设备在已固化设备列表中不存在,开启常规ble蓝牙连接流程。
    // 订阅BLE设备发现
    this.onBLEDeviceFindTwo();
    // 过滤参数可根据实际场景进行设置
    let scanFilter: ble.ScanFilter = {
      name: 'name'
    };
    // 扫描参数可根据实际场景配置
    let scanOptions: ble.ScanOptions = {
      interval: 500,
      dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER,
      matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE,
    };
    ble.startBLEScan([scanFilter], scanOptions);
  }

  onBLEDeviceFindTwo() {
    ble.on('BLEDeviceFind', (data: Array<ble.ScanResult>) => {
      // 发现设备,创建实例进行连接。
      this.gattClient = ble.createGattClientDevice(data[0].deviceId);
      // 连接前先创建连接状态回调监听
      this.onBLEConnectionStateChangeTwo();
      // 连接ble蓝牙
      this.gattClient.connect();
      // 连接成后,将已连接设备加入固化列表。
      // 注意:加入固化列表时机并不固定,可根据实际场景来进行变更。
      access.addPersistentDeviceId(data[0].deviceId);
    });
  }

  onBLEConnectionStateChangeTwo() {
    this.gattClient?.on('BLEConnectionStateChange', (state: ble.BLEConnectionChangeState) => {
      if (state.state === constant.ProfileConnectionState.STATE_DISCONNECTED) {
        // 如果蓝牙意外断开了连接,可以在此处重新发起连接,以达到意外断连快速回连能力。
        // 不过需要保证此时this.gattClient实例没有被销毁。
        this.gattClient?.connect();
      }
    });
  }

  build() {
    Column() {
      Button('ble蓝牙连接/支持快速回连(方案1)').onClick(() => {
        // 发起连接
        this.ReconnectOne();
      });
      Button('ble蓝牙连接/支持快速回连(方案2)').onClick(() => {
        // 发起连接
        this.ReconnectTwo();
      });
    }.height('100%')
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround);
  }
}

更多关于HarmonyOS鸿蒙Next中如何实现蓝牙BLE设备的稳定连接与数据收发?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


鸿蒙通过 @ohos:bluetooth 模块提供完整 BLE 支持:

  • 使用 createBLEConnection 建立连接,注册 onBLEConnectionStateChange 监听断连;
  • 通过 startNotify 订阅特征值通知,onBLECharacteristicChanged 接收数据;
  • 保活策略:在 module.json5 中声明 bluetooth 权限,并申请 continuousTask 防止后台被杀;
backgroundTaskManager.startBackgroundRunning 开启长时间任务

在HarmonyOS Next中,实现蓝牙BLE稳定连接与数据收发,需使用@ohos.bluetooth API。首先通过bluetooth.BLE.createGattClientDevice()发现并创建设备对象,调用connect()建立连接。连接成功后,通过getServices()发现服务与特征值。使用readCharacteristicValue()writeCharacteristicValue()setCharacteristicChangeNotification()进行数据收发。为确保稳定,需在on('BLEConnectionStateChange')监听连接状态,并在on('BLECharacteristicChange')监听特征值变化。注意在应用退出或断开时调用disconnect()closeGattClient()释放资源。

在HarmonyOS Next中,实现BLE设备的稳定连接与后台数据收发,核心在于正确使用新的@ohos.bluetooth API并遵循其生命周期管理。以下是关键点:

  1. 后台持续通信:是支持的。您需要申请ohos.permission.APPROXIMATELY_LOCATIONohos.permission.KEEP_BACKGROUND_RUNNING权限,并在module.json5中配置长时任务(LongTimeTask)。在应用挂起(Suspend)前,应通过bluetooth.startBLEScan()保持扫描或通过bluetooth.createGattClientDevice()建立的连接来维持后台活动。连接本身在后台可保持,但数据收发需在后台任务生命周期内完成。

  2. 稳定连接与防断开

    • 连接管理:使用createGattClientDevice()建立连接后,务必通过device.on('BLEConnectionStateChange')监听连接状态变化。在回调中处理断开事件,实现自动重连逻辑(例如,指数退避重试)。
    • 参数优化:在BLEConnectionParameters中可调整连接间隔(如connectionInterval),更短的间隔(如7.5ms-15ms)可提升吞吐量和实时性,但会增加功耗。需根据手环数据频率做平衡。
    • 绑定与配对:对于需要更高安全性的设备,在首次连接后调用device.setBondedDevices()进行绑定,系统会保存绑定信息,后续连接更稳定。
    • 系统策略应对:系统为省电可能关闭蓝牙。可通过bluetooth.on('stateChange')监听蓝牙开关状态,并提示用户保持开启。
  3. 数据收发实践

    • 服务与特征值操作:连接后,使用device.readCharacteristicValue()device.writeCharacteristicValue()device.setNotifyCharacteristicChanged()进行数据交互。对于心率数据,通常需要订阅(Notify)特定特征值的变化。
    • 数据缓存与处理:在后台接收数据时,应尽快处理或序列化存储,避免阻塞。考虑使用Worker线程处理数据解析,确保UI流畅。

示例代码片段(连接与订阅)

import { bluetooth } from '@ohos.bluetooth';

// 1. 发现设备后,创建Gatt客户端设备
let device: bluetooth.GattClientDevice = bluetooth.createGattClientDevice(deviceId);

// 2. 监听连接状态
device.on('BLEConnectionStateChange', (state: bluetooth.ConnectionState) => {
  if (state === bluetooth.ConnectionState.STATE_DISCONNECTED) {
    // 触发重连逻辑
    reconnectDevice(device);
  }
});

// 3. 连接设备
device.connect();

// 4. 发现服务后,订阅特征值通知
device.setNotifyCharacteristicValue(serviceUuid, characteristicUuid, true);

// 5. 监听特征值变化
device.on('BLECharacteristicChange', (characteristicValue: bluetooth.CharacteristicReadResult) => {
  // 解析心率数据
  let heartRate = parseHeartRate(characteristicValue.value);
});

总结:鸿蒙Next的BLE API提供了完整的后台支持能力。稳定性的关键在连接状态监听与自动重连合理的连接参数以及正确的后台权限与任务配置。对于健康手环这类持续数据场景,建议在断开回调中实现稳健的重连机制,并优化连接间隔以平衡功耗与实时性。

回到顶部