杰理 SDK 文档中心
首页
首页
  • 项目概览

    • 项目简介与核心能力
    • 快速开始
    • 工程结构与依赖库
  • 核心功能

    • RCSP OTA 升级流程
    • BLE 升级通道
    • SPP 升级通道
    • 自动回连机制
  • 蓝牙通信架构

    • 蓝牙抽象层与基础组件
    • BLE 模块实现
    • SPP 模块实现
    • 蓝牙管理与 OTA 管理器
  • 示例应用

    • 应用入口与启动流程
    • 主界面与设备连接交互
    • 关于、日志与辅助页面
  • 调试与运维

    • 日志系统与调试技巧
    • 问题排查与技术支持
  • 开发者指南

    • SDK 版本历史
    • 集成与二次开发指南

蓝牙管理与 OTA 管理器

本文档深入剖析 HarmonyOS-JL_OTA 工程中「管理器层」的两个核心类:BluetoothManager(蓝牙连接/扫描的双栈门面)与 BluetoothOTAManager(OTA 升级编排器),说明它们如何把底层 BLE/SPP 实现与杰理 rcsp OTA SDK 桥接成一个完整的"扫描 → 连接 → 升级 → 回连"闭环。

Purpose and Scope

本页面覆盖以下内容:

  • BluetoothManager:单例门面,按设备类型(BleDevice / SppDevice)把扫描、连接、断开、状态查询等操作分发到 BleImpl / SppImpl。
  • BluetoothOTAManager:OTA 升级的总编排器,负责初始化事件订阅、构造 OTAWrapperOption 适配层、启动升级、桥接蓝牙回调到 rcsp SDK,以及实现可选的"自定义回连"逻辑。
  • 支撑两者的基础契约:IConnect、ConnectState、ConnectStateInfo、BaseConnectEventTypeConstant。

以下主题属于兄弟页面,不在本页展开:

  • BLE 具体实现(BleImpl、BleDevice、扫描/连接配置)→ 见「BLE 协议实现」相关页面。
  • SPP 具体实现(SppImpl、SppDevice、Socket 配置)→ 见「SPP 协议实现」相关页面。
  • rcsp SDK 侧封装(OTAWrapper、OTAWrapperOption、Reconnect、IOTAWrapper)→ 见「OTA 升级 SDK 封装」相关页面。

Overview

在 JL_OTA 的蓝牙架构中,管理器层位于应用与协议实现之间,承担三个职责:

  1. 协议无关的门面:BluetoothManager 向应用暴露统一的"扫描/连接/断开/状态查询"API,内部通过 instanceof 判断设备类型,把调用分发给 BLE 或 SPP 实现。应用侧无需关心设备走的是 GATT 还是 RFCOMM。
  2. 事件中枢:BluetoothOTAManager 在 init() 时订阅 BleImpl / SppImpl 的全部关键事件(扫描状态、发现设备、连接状态、特征值变化、SPP 数据读取),再统一转发给 rcsp SDK 的 OTAWrapper,完成"蓝牙世界"与"RCSP 协议世界"的解耦。
  3. 升级编排:startOTA() 负责组装 JL_OTA.OtaConfig、包装升级回调(OTAUpgradeCallback),并在需要时驱动"自定义回连"流程——通过 Reconnect 类在升级中断后重新扫描、识别、连接设备,最后把新的 deviceId 回传给 SDK 恢复升级。

整个设计遵循**桥接(Bridge)+ 门面(Facade)+ 策略分发(instanceof dispatch)**的组合模式:管理器是唯一入口,协议差异被封装在 impl 层,OTA 协议逻辑完全由 rcsp SDK 承载,管理器只做翻译与路由。

Architecture

flowchart TD
    subgraph sg_App["应用层 (App/UI)"]
        App["页面 / 业务代码"]
    end

    subgraph sg_Manager["管理器层 (Managers)"]
        BTM["BluetoothOTAManager"]
        BM["BluetoothManager (单例 bluetoothInstance)"]
    end

    subgraph sg_Impl["协议实现层 (Impl)"]
        BleImpl["BleImpl"]
        SppImpl["SppImpl"]
    end

    subgraph sg_Base["基础抽象层 (base)"]
        IConnect["IConnect 接口"]
        IScan["IScan 接口"]
        BTDevice["BluetoothDevice"]
    end

    subgraph sg_OTA["OTA SDK 层 (rcsp)"]
        OTAWrapper["OTAWrapper"]
        Reconnect["Reconnect"]
    end

    subgraph sg_Sys["HarmonyOS 系统能力"]
        BLE["蓝牙 BLE (GATT)"]
        SPP["蓝牙 SPP (RFCOMM)"]
    end

    App -->|"startOTA / 扫描 / 连接"| BTM
    App -->|"扫描 / 连接 / 收发"| BM
    BTM -->|"复用单例"| BM
    BTM -->|"OTAWrapperOption 适配回调"| OTAWrapper
    OTAWrapper -->|"升级事件转发"| BTM
    BTM -->|"自定义回连"| Reconnect
    BM -->|"设备类型分发"| BleImpl
    BM -->|"设备类型分发"| SppImpl
    BleImpl -.->|"实现"| IConnect
    BleImpl -.->|"实现"| IScan
    SppImpl -.->|"实现"| IConnect
    SppImpl -.->|"实现"| IScan
    BleImpl --> BLE
    SppImpl --> SPP

架构解读:

  • BluetoothManager(BluetoothManager.ets)在构造时即创建 BleImpl 与 SppImpl,并导出进程级单例 bluetoothInstance,供 BluetoothOTAManager 与业务页面共享——保证全工程只有一套蓝牙连接状态机。
  • BluetoothOTAManager 不直接持有 impl 引用,而是通过单例访问,并在 init() 中注册回调;回调经过内部 xxxCallbackFun 箭头函数包装后统一转发给 otaWrapper。
  • 数据流向是双向的:应用 → 管理器 → impl → 系统蓝牙(下行);系统蓝牙 → impl 事件 → 管理器回调 → OTAWrapper(上行)。

BluetoothManager:双栈门面与设备类型分发

单例与成员

BluetoothManager 的成员极简:两个协议实现 + 一个"当前通讯方式"标记。默认通讯方式为 "BLE"(源码 L17-L26):

export class BluetoothManager {
  private _bleImpl: BleImpl
  private _sppImpl: SppImpl
  // 当前通讯方式
  private _communicationWay: BluetoothDeviceType = "BLE"

  constructor() {
    this._bleImpl = new BleImpl()
    this._sppImpl = new SppImpl()
  }

communicationWay 提供 getter/setter(BluetoothDeviceType 取值如 "BLE" / "EDR"),它是"未显式指定通讯方式时"的兜底默认值;bleImpl / sppImpl 两个 getter 暴露具体实现供上层直接调用(如 bluetoothInstance.bleImpl.sendData(...))。

按设备类型分发的核心模式

连接与断开 API 采用 instanceof 策略分发——这是整个门面最核心的设计意图:调用方只传 BluetoothDevice 抽象,由管理器根据真实子类决定走哪条协议栈(源码 L44-L61):

  connect(device: BluetoothDevice, success?: Callback<BluetoothDevice>,
    fail?: Callback<BusinessError>) {
    if (device instanceof SppDevice) { //根据设备类型判断-Spp
      this.sppImpl.connect(device, success, fail)
    } else if (device instanceof BleDevice) { //根据设备类型判断-Ble
      this.bleImpl.connect(device, success, fail)
    } else { //无法判断设备类型
    }
  }

  disconnect(device: BluetoothDevice) {
    if (device instanceof SppDevice) { //根据设备类型判断-Spp
      this.sppImpl.disconnect(device)
    } else if (device instanceof BleDevice) { //根据设备类型判断-Ble
      this.bleImpl.disconnect(device)
    } else { //无法判断设备类型
    }
  }

注意 else 分支为空——若传入既非 SppDevice 也非 BleDevice 的对象(例如直接 new BluetoothDevice()),调用会被静默忽略。这是设计上刻意的"防御性空操作",避免向错误协议栈发起连接。

通讯方式回退逻辑

查询类与扫描类 API(getConnectedDevice / isScanning / startScan / refreshScan / stopScan)允许调用方通过可选参数 type 指定通讯方式;不指定时回退到 _communicationWay。以 startScan 为例(源码 L113-L123):

  startScan(scanTimeOut?: number, type?: BluetoothDeviceType | undefined): void {
    let communicationWay = type
    if (communicationWay == undefined) { //未明指通讯方式
      communicationWay = this._communicationWay
    }
    if (communicationWay === "EDR") {
      return this.sppImpl.startScan(scanTimeOut)
    } else if (communicationWay === "BLE") {
      return this.bleImpl.startScan(scanTimeOut)
    }
  }

这套回退机制让 BluetoothOTAManager 在升级回连时能同时启动两种扫描(见后文 sanDevice),而普通应用只需设置一次 communicationWay 即可。

BluetoothOTAManager:OTA 升级编排器

BluetoothOTAManager(BluetoothOTAManager.ets)是连接蓝牙层与 rcsp SDK 的"翻译官"。它维护 5 个私有回调包装器(scanStateCallbackFun、deviceFindCallbackFun、connectStateCallbackFun、characteristicInfoCallbackFun、sppDataReadInfoCallbackFun),一个 _ReconnectMap(设备维度到 Reconnect 实例的映射,仅自定义回连时使用),以及 otaWrapperOption / otaWrapper 两个核心成员。

init():两段式初始化

init() 顺序执行 initBluetooth() 与 initRcsp()(源码 L50-L55)。先订阅蓝牙事件、再构建 SDK 适配层——顺序不可颠倒,因为 OTAWrapper 的回连逻辑依赖扫描/连接事件已经就绪。

initBluetooth() 通过单例 bluetoothInstance 订阅 6 类事件(源码 L57-L68):

  private initBluetooth() {
    this.bluetoothInstance.bleImpl.on(ScanEventType.SCAN_STATE_CHANGE, this.scanStateCallbackFun)
    this.bluetoothInstance.bleImpl.on(ScanEventType.SCAN_DEVICE_FIND, this.deviceFindCallbackFun)
    this.bluetoothInstance.bleImpl.on(BaseConnectEventTypeConstant.CONNECT_STATE_CHANGE, this.connectStateCallbackFun)
    this.bluetoothInstance.bleImpl.on(BleConnectEventTypeConstant.CONNECT_BLE_CHARACTERISTIC_CHANGE,
      this.characteristicInfoCallbackFun)

    this.bluetoothInstance.sppImpl.on(BaseConnectEventTypeConstant.CONNECT_STATE_CHANGE, this.connectStateCallbackFun)
    this.bluetoothInstance.sppImpl.on(SppConnectEventTypeConstant.CONNECT_DATA_READ_CHANGE,
      this.sppDataReadInfoCallbackFun)
  }

设计要点:连接状态事件(CONNECT_STATE_CHANGE)被 BLE 与 SPP 共享同一个回调,因为上层只关心"连上/失败/断开"三种语义,协议栈差异在 impl 层已归一化为 ConnectStateInfo<BTDevice>。

OTAWrapperOption:蓝牙能力对 SDK 的适配面

initRcsp() 构造一个实现了全部蓝牙侧能力的 OTAWrapperOption 对象,随后 new OTAWrapper(otaWrapperOption)(源码 L70-L124):

  private initRcsp() {
    this.otaWrapperOption = {
      /**是否需要认证**/
      isUseAuth: (): boolean => {
        return true
      },
      isInnerReconnect: () => {
        return true
      },
      /**扫描设备**/
      sanDevice: () => {
        //升级成功-扫描的是平时设备。升级中 -扫描的是BLE设备
        this.bluetoothInstance.startScan(10 * 1000, "BLE")
        if (this.bluetoothInstance.communicationWay == "EDR") {
          this.bluetoothInstance.startScan(10 * 1000, "EDR")
        }
      },
      /**连接设备**/
      connectDevice: (device: OTADevice) => {
        //目前回连只有BLE设备
        const isBle = true
        const deviceId = device.deviceId
        if (isBle) {
          this.bluetoothInstance.connect(new BleDevice(deviceId))
        } else {
          this.bluetoothInstance.connect(new SppDevice(deviceId))
        }
      },
      /**断开设备**/
      disconnectDevice: (device: OTADevice) => {
        // 此处需判断设备类型
        const isBle =
          this.bluetoothInstance.bleImpl.getConnectedDevice().findIndex(item => item.deviceId == device.deviceId) != -1
        const deviceId = device.deviceId
        if (isBle) {
          this.bluetoothInstance.disconnect(new BleDevice(deviceId))
        } else {
          this.bluetoothInstance.disconnect(new SppDevice(deviceId))
        }
      },
      /**发送数据**/
      sendData: (device: OTADevice, data: Uint8Array) => {
        // 此处需判断设备类型
        const isBle =
          this.bluetoothInstance.bleImpl.getConnectedDevice().findIndex(item => item.deviceId == device.deviceId) != -1
        const deviceId = device.deviceId
        if (isBle) {
          this.bluetoothInstance.bleImpl.sendData(new BleDevice(deviceId), RCSP_UUID_SERVICE, RCSP_UUID_WRITE, data)
        } else {
          this.bluetoothInstance.sppImpl.sendData(new SppDevice(deviceId), RCSP_SOCKET_UUID, data)
        }
      }
    }
    this.otaWrapper = new OTAWrapper(this.otaWrapperOption)
  }

这一段是理解整个桥接的关键:

  • sanDevice 固定以 10 秒超时启动 BLE 扫描,且当当前通讯方式为 EDR 时并行启动 SPP 扫描——因为升级过程中设备可能以 BLE 广播出现,平时则以经典蓝牙出现。
  • connectDevice 目前硬编码走 BLE(const isBle = true),注释明确"目前回连只有 BLE 设备",EDR 分支为未来扩展保留。
  • disconnectDevice / sendData 则通过查询 bleImpl.getConnectedDevice() 是否包含该 deviceId 来动态判定协议栈,而非硬编码——sendData 在 BLE 通道写入 RCSP_UUID_SERVICE 服务的 RCSP_UUID_WRITE 特征值,在 SPP 通道写入 RCSP_SOCKET_UUID 对应 Socket。
  • isUseAuth 返回 true 表示 RCSP 协议需完成设备认证;isInnerReconnect 返回 true 表示使用 SDK 内部回连(关闭时才会走本管理器的自定义回连分支)。

startOTA():升级入口与回调包装

startOTA(deviceId, otaCallback) 是升级的唯一入口(源码 L126-L252)。它做三件事:

  1. 组装升级配置:new JL_OTA.OtaConfig() 并设置 isSupportNewRebootWay = true(启用"新回连方式":设备重启后通过特殊广播包携带自身 BLE 地址,供回连识别)。
  2. 包装回调:构造 tempOtaUpgradeCallback,把 SDK 的每个回调事件透传给调用方,并在关键节点插入回连管理逻辑(见下节)。
  3. 启动升级:this.otaWrapper?.startOTA(new OTADevice(deviceId), otaConfig, tempOtaUpgradeCallback)。

回调包装中值得注意的行为:

  • onNeedReconnect:当 SDK 判定需要回连时触发。若 isInnerReconnect() == false,则进入自定义回连流程——用 reConnectMsg.deviceBleMac 构造目标识别规则,实例化 Reconnect 存入 _ReconnectMap 并启动(超时为 JL_OTA.OTAImpl.RECONNECT_DEVICE_TIMEOUT)。同时原始回调也被透传,业务方可自行处理。
  • onStopOTA / onCancelOTA / onError:除了透传,还会从 _ReconnectMap 取出对应 Reconnect 并调用 stopReconnect(),防止升级结束后回连任务残留。
  • onReadData(offset, size):透传给调用方,用于从固件文件按偏移读取分片数据。
  • onRCSPInit(deviceId, isInit):RCSP 初始化成功后,遍历 _ReconnectMap 通知所有 Reconnect "设备已连接"(onDeviceConnected(new BluetoothDevice(deviceId))),推动回连状态机前进。

蓝牙事件 → SDK 事件桥接

管理器把 5 类蓝牙回调翻译成 SDK 能理解的事件:

蓝牙事件桥接方法转发目标
扫描结束 SCAN_STATE_FINISHonScanStateotaWrapper.onSanDeviceStop() + 各 Reconnect.onScanStop()
发现设备onDeviceFindotaWrapper.onScanFound(foundDevs) + Reconnect.onDiscoveryDevices(...)
连接成功onConnectStateChangeotaWrapper.onConnectStateSuccess(...)
连接失败onConnectStateChangeotaWrapper.onConnectStateFailed(...) + Reconnect.onDeviceConnectFailed(...)
连接断开onConnectStateChangeotaWrapper.onConnectStateDisconnect(...) + Reconnect.onDeviceConnectDisconnected(...)
BLE Notify 数据onBLECharacteristicInfo若 characteristicUuid === RCSP_UUID_NOTIFY 则 otaWrapper.onReceiveData(...)
SPP 数据读取onSppDataReadInfo若 uuid === RCSP_SOCKET_UUID 则 otaWrapper.onReceiveData(...)

onDeviceFind 的转换逻辑(源码 L268-L280)展示了设备模型的适配:扫描到的 BleDevice 被转成 rcsp 侧 OTADevice,并把广播原始数据 scanResult?.data 挂到 advertiseData 上——这正是回连时解析"新回连广播包"所需的数据来源。

数据上行同理(源码 L304-L322):BLE 通道只接受 RCSP_UUID_NOTIFY 特征值变化,SPP 通道只接受 RCSP_SOCKET_UUID 的数据,二者最终都汇入 otaWrapper.onReceiveData(new OTADevice(deviceId), new Uint8Array(data)),由 SDK 完成 RCSP 协议解析。

自定义回连(Reconnect)识别算法

当 SDK 内部回连被禁用时,onNeedReconnect 内构造 ReconnectOp,其 isReconnectDevice 是回连成败的核心判定(源码 L141-L194):

  • 新回连方式(reConnectMsg.isSupportNewReconnectADV == true):在扫描设备的广播数据中查找魔数 "D60541544F4C4A"(即 JL 厂商私有广播标识),命中后取其后 6 字节做字节反转得到设备真实 BLE MAC,与升级前记录的 oldDeviceMac(deviceBleMac 去冒号、大写)比对。同时用"广播包含旧 MAC / MAC 前缀模糊匹配"做日志优化,避免海量打印。
  • 旧回连方式:直接比对扫描到的 deviceId 与升级前 deviceId(含前 10 位前缀模糊匹配日志)。

回连成功回调 onReconnectSuccess 会 _ReconnectMap.delete(deviceId) 并调用 reconnectCallback.onResult(device.deviceId)——把新 deviceId 交还 SDK,SDK 据此继续后续升级流程;失败则 onError(ERR_OTA_RECONNECT_DEVICE_TIMEOUT, ...)。

Core Flow:一次完整的 OTA 升级旅程

下面用时序图展示从应用调用 init() 到升级完成的完整控制流。图中可见管理器层如何作为"双通道翻译器"工作:

sequenceDiagram
    participant App as 应用 / 页面
    participant BTM as BluetoothOTAManager
    participant BM as BluetoothManager (单例)
    participant Impl as BleImpl / SppImpl
    participant OW as OTAWrapper (rcsp SDK)
    participant Dev as 蓝牙设备

    App->>BTM: init()
    BTM->>BM: 订阅扫描/连接/特征值/SPP数据事件
    BTM->>OW: new OTAWrapper(otaWrapperOption)
    Note over OW: SDK 持有蓝牙能力适配回调 (sanDevice/connectDevice/sendData)

    App->>BTM: startOTA(deviceId, otaCallback)
    BTM->>OW: startOTA(OTADevice, OtaConfig, 包装回调)
    OW->>BM: sendData(deviceId, RCSP_UUID_WRITE, 升级指令)
    BM->>Impl: instanceof 分发到 BleImpl
    Impl->>Dev: GATT 写入特征值
    Dev-->>Impl: Notify 特征值变化
    Impl-->>BTM: onBLECharacteristicInfo (RCSP_UUID_NOTIFY)
    BTM->>OW: onReceiveData(device, data)
    Note over OW: RCSP 协议解析 -> 进度回调
    OW-->>App: onProgress(type, progress)

    Note over OW,BTM: 设备重启,需要回连
    OW->>BTM: onNeedReconnect(reConnectMsg, reconnectCallback)
    BTM->>BM: sanDevice() -> startScan(10s, BLE/EDR)
    BM->>Impl: 启动扫描
    Impl-->>BTM: 发现设备 (SCAN_DEVICE_FIND)
    BTM->>OW: onScanFound(devList)
    Note over BTM: 自定义回连时 Reconnect.isReconnectDevice 判定
    BTM->>BM: connectDevice -> connect(new BleDevice(deviceId))
    Impl-->>BTM: CONNECT_STATE_SUCCESS
    BTM->>OW: onConnectStateSuccess(device)
    BTM-->>OW: reconnectCallback.onResult(newDeviceId)
    OW-->>App: 继续升级 / onStopOTA / onError

自定义回连决策流

当 isInnerReconnect() == false 时,回连完全由管理器接管。其判定分支如下:

flowchart TD
    Start([升级中断需回连]) --> Check{"isInnerReconnect() == true?"}
    Check -->|"是"| SDK["SDK 内部回连 (不创建 Reconnect)"]
    Check -->|"否"| Custom["new Reconnect(op, callback) 存入 _ReconnectMap"]
    Custom --> Scan["sanDevice() 启动 BLE(+EDR) 扫描 10s"]
    Scan --> Find{"发现设备 isReconnectDevice?"}
    Find -->|"新回连广播"| New{"广播含 D60541544F4C4A?"}
    New -->|"是"| Parse["解析字节反转 MAC 与 oldDeviceMac 比对"]
    New -->|"否"| ReScan["继续扫描"]
    Parse --> Match{"MAC 命中?"}
    Match -->|"是"| Connect["connectDevice 建立连接"]
    Match -->|"否"| ReScan
    Find -->|"旧方式"| Old{"deviceId 相等?"}
    Old -->|"是"| Connect
    Old -->|"否"| ReScan
    Connect --> Success{"onReconnectSuccess?"}
    Success -->|"是"| Notify["onResult(newDeviceId) 通知 SDK"]
    Success -->|"否/超时"| Fail["onError ERR_OTA_RECONNECT_DEVICE_TIMEOUT<br/>并从 _ReconnectMap 移除"]
    Notify --> Done([升级继续])
    Fail --> Done
    ReScan --> Timeout{"RECONNECT_DEVICE_TIMEOUT?"}
    Timeout -->|"是"| Fail
    Timeout -->|"否"| ReScan

关键时序约束:onRCSPInit 中 _ReconnectMap.forEach(reconnect => reconnect.onDeviceConnected(...)) 必须在连接成功且 RCSP 初始化完成后才执行,否则 Reconnect 状态机无法推进到"已连接"阶段。

事件与接口模型

管理器依赖的基础契约定义在 base/IConnect.ets:

classDiagram
    class IConnectSettingConfigure {
        <<interface>>
    }

    class IConnect~T, C~ {
        <<interface>>
        +connect(device, success?, fail?) void
        +disconnect(device) void
        +setConnectSettingConfigure(config) void
        +getConnectSettingConfigure() C
        +getConnectedDevice() Array~T~
        +isConnecting(device) boolean
        +isConnected(device) boolean
        +on(type, callback) void
        +off(type, callback?) void
    }

    class ConnectStateInfo~T~ {
        +device T
        +state ConnectState
        +error? BusinessError
    }

    class ConnectState {
        <<enumeration>>
        CONNECT_STATE_DISCONNECT
        CONNECT_STATE_SUCCESS
        CONNECT_STATE_FAILED
    }

    IConnect~T, C~ --> ConnectStateInfo~T~ : 回调负载
    ConnectStateInfo~T~ --> ConnectState : state
    ConnectStateInfo~T~ ..> IConnectSettingConfigure : 泛型约束 C

契约要点:

  • ConnectState 是三态枚举(DISCONNECT / SUCCESS / FAILED,见 IConnect.ets L21-L28),BLE 与 SPP 实现共享同一语义,这是 BluetoothOTAManager 能用同一个 connectStateCallbackFun 订阅两类实现的前提。
  • ConnectStateInfo<T> 携带 device、state 与可选的 error: BusinessError,失败原因随事件一并送达。
  • IConnect<T, C> 是泛型接口:T 为设备类型(BleDevice / SppDevice),C 为连接配置(IConnectSettingConfigure 标记接口),由各 impl 实现具体行为。
  • BaseConnectEventTypeConstant.CONNECT_STATE_CHANGE = 'connectStateChange' 是 BLE/SPP 共用的连接事件名常量;off 支持不传回调以取消该类型全部订阅。

数据模型与关键常量

常量 / 类型定义位置说明
bluetoothInstanceBluetoothManager.ets L152进程级单例,BluetoothOTAManager 与业务共用
RCSP_UUID_SERVICE / RCSP_UUID_WRITE / RCSP_UUID_NOTIFYble/BleConnectSettingConfigure.etsRCSP GATT 服务/写/通知特征值,升级数据下行与上行通道
RCSP_SOCKET_UUIDspp/SppConnectSettingConfigure.etsRCSP SPP Socket 标识
_ReconnectMap: Map<string, Reconnect>BluetoothOTAManager.ets L33以 deviceId 为键的回连任务表,支持多设备并发回连
ScanStateInfo / BleDevicebase/IScan.ets / ble/BleDevice.ets扫描状态与扫描结果模型,advertiseData 承载广播原始字节

Usage Examples

示例 1:初始化管理器并启动 OTA 升级

业务方在页面 aboutToAppear 中初始化管理器,之后即可发起升级(调用方式取自源码对 init() / startOTA() 的公开定义,见 BluetoothOTAManager.ets L50-L55 与 L126-L141):

// 初始化:订阅蓝牙事件 + 构造 OTAWrapper
otaManager.init()

// 启动升级
otaManager.startOTA(deviceId, {
  onStartOTA: () => { /* UI: 显示升级中 */ },
  onProgress: (type, progress) => { /* UI: 更新进度条 */ },
  onNeedReconnect: (reConnectMsg, reconnectCallback) => {
    // 业务方可在此展示"设备重启,正在回连…"
  },
  onReadData: (offset, size) => { /* 按偏移返回固件分片 Uint8Array */ },
  onStopOTA: () => { /* 升级完成 */ },
  onCancelOTA: () => { /* 用户取消 */ },
  onError: (error, message) => { /* 升级失败 */ }
})

示例 2:利用单例直接进行扫描与连接

应用不经过 OTA 时,可直接使用 BluetoothManager 单例做设备发现与连接(签名见 BluetoothManager.ets L44-L52 与 L113-L123):

import { bluetoothInstance } from './bluetooth/BluetoothManager'
import { BleDevice } from './bluetooth/ble/BleDevice'

// 默认通讯方式为 BLE,亦可显式指定
bluetoothInstance.communicationWay = "BLE"

bluetoothInstance.startScan(10 * 1000)              // 扫描 10 秒
// 扫描到设备后:
const device = new BleDevice("AA:BB:CC:DD:EE:FF")
bluetoothInstance.connect(device,
  (dev) => { /* 连接成功 */ },
  (err) => { /* 连接失败: BusinessError */ })

示例 3:订阅连接状态事件

连接状态统一通过 CONNECT_STATE_CHANGE 事件获取,BLE 与 SPP 共用(事件常量与负载定义见 IConnect.ets L14-L42):

import { BaseConnectEventTypeConstant, ConnectState } from './base/IConnect'

bluetoothInstance.bleImpl.on(BaseConnectEventTypeConstant.CONNECT_STATE_CHANGE, (info) => {
  switch (info.state) {
    case ConnectState.CONNECT_STATE_SUCCESS:   // 连接成功
    case ConnectState.CONNECT_STATE_FAILED:    // 连接失败(info.error 携带原因)
    case ConnectState.CONNECT_STATE_DISCONNECT // 连接断开
  }
})

Configuration Options

BluetoothOTAManager 的可配置面集中在 OTAWrapperOption 适配层(BluetoothOTAManager.ets L70-L124):

配置项类型默认行为说明
isUseAuth() => booleantrue是否启用 RCSP 设备认证
isInnerReconnect() => booleantruetrue 用 SDK 内部回连;false 走 Reconnect 自定义回连
sanDevice() => void10s 超时 startScan("BLE"),EDR 时并行扫SDK 要求重新扫描设备时调用
connectDevice(device) => void构造 BleDevice 并 connect回连时建立连接(当前固定 BLE)
disconnectDevice(device) => void查询已连接列表判定 BLE/SPP 后断开升级前断开旧连接
sendData(device, data) => voidBLE 写 RCSP_UUID_WRITE / SPP 写 RCSP_SOCKET_UUIDRCSP 指令下行通道

升级参数相关:

配置项位置默认说明
OtaConfig.isSupportNewRebootWaystartOTA() 内true启用新回连方式(广播携带 BLE 地址)
RECONNECT_DEVICE_TIMEOUTstartOTA() 内JL_OTA.OTAImpl 常量自定义回连总超时
扫描超时sanDevice 内10 * 1000 ms回连扫描窗口

API Reference

BluetoothManager(BluetoothManager.ets)

方法签名说明
connect(device: BluetoothDevice, success?: Callback<BluetoothDevice>, fail?: Callback<BusinessError>): void按 instanceof 分发到 SPP/BLE 连接;未知类型静默忽略
disconnect(device: BluetoothDevice): void按设备类型分发断开
getConnectedDevice(type?: BluetoothDeviceType): Array<BluetoothDevice> | undefined未传 type 时回退 communicationWay;未知类型返回 undefined
isConnecting(device: BluetoothDevice): boolean未知类型返回 false
isConnected(device: BluetoothDevice): boolean未知类型返回 false
isScanning(type?: BluetoothDeviceType): boolean按通讯方式查询
startScan(scanTimeOut?: number, type?: BluetoothDeviceType): void按通讯方式启动扫描
refreshScan(type?: BluetoothDeviceType): void刷新扫描
stopScan(type?: BluetoothDeviceType): void停止扫描

属性:communicationWay(读写,默认 "BLE")、bleImpl(只读)、sppImpl(只读)。

BluetoothOTAManager(BluetoothOTAManager.ets)

方法签名说明
init(): void订阅蓝牙事件 + 构造 OTAWrapper,须在 startOTA 前调用
startOTA(deviceId: string, otaCallback: OTAUpgradeCallback): void组装 OtaConfig、包装回调、启动升级;含自定义回连分支
getOTAWrapper(): IOTAWrapper | undefined供上层直接访问 SDK 包装器(如手动调用 onReceiveData 调试)

私有桥接回调(由 initBluetooth 注册):onScanState、onDeviceFind、onConnectStateChange、onBLECharacteristicInfo、onSppDataReadInfo。

IConnect<T, C>(IConnect.ets L45-L75)

  • connect(device: T, success?: Callback<T>, fail?: Callback<BusinessError>): void
  • disconnect(device: T): void
  • setConnectSettingConfigure(config: C): void / getConnectSettingConfigure(): C
  • getConnectedDevice(): Array<T>
  • isConnecting(device: T): boolean / isConnected(device: T): boolean
  • on(type: BaseConnectEventTypeConstant.CONNECT_STATE_CHANGE, callback: Callback<ConnectStateInfo<T>>): void
  • off(type, callback?): void — 不传 callback 时取消该类型全部订阅

Failure Modes、边界与并发

设备类型不可判定

BluetoothManager.connect/disconnect/isConnecting/isConnected 对既非 SppDevice 也非 BleDevice 的对象走空分支:连接被静默忽略、状态查询返回 false(见 BluetoothManager.ets L44-L96)。调用方必须使用 BleDevice / SppDevice 的具体子类构造设备,不能传基类实例——这是 instanceof 分发方案的硬约束。

回连失败与超时

  • 自定义回连在 RECONNECT_DEVICE_TIMEOUT 内未命中目标设备时,Reconnect 触发 onReconnectFailed,管理器透传 JL_OTA.OtaError.ERR_OTA_RECONNECT_DEVICE_TIMEOUT 错误并清理 _ReconnectMap 条目(BluetoothOTAManager.ets L205-L210)。
  • 回连期间设备连接失败/断开(onDeviceConnectFailed / onDeviceConnectDisconnected)会再次调用 sanDevice() 重新扫描,形成"扫描→识别→连接→失败→再扫描"的重试循环,直到超时。
  • 新回连方式下若 RCSP 未从协议层拿到设备 BLE 地址(oldDeviceMac 为空),isReconnectDevice 直接返回 false,只能依赖旧方式匹配 deviceId。

升级生命周期中的资源清理

onStopOTA / onCancelOTA / onError 三个回调都会执行 _ReconnectMap.get(deviceId)?.stopReconnect()(BluetoothOTAManager.ets L226-L240)。如果业务方在收到 onError 后不结束页面,务必保证该回调被 SDK 触发,否则回连任务可能残留并继续扫描。

并发与多设备

  • _ReconnectMap 以 deviceId 为键支持多设备并发回连;回调中通过 forEach 遍历,属于"读多写少"场景。ArkTS 事件回调运行在单线程事件循环上,delete 与 forEach 不会交错执行,因此无需额外加锁;但不应在回调中同步调用 startOTA 修改同一设备。
  • 扫描回调(onScanState / onDeviceFind)与连接回调(onConnectStateChange)可能在同一扫描窗口内频繁触发,管理器只做转发与状态机推进,不做节流——高频事件下的性能取决于 BleImpl / SppImpl 的缓冲队列(BufferQueue / BaseSendDataHandler 属于兄弟页面范畴)。

性能与运维注意

  • 扫描开销:sanDevice 每次回连都会启动 10 秒扫描,且 EDR 模式下 BLE + SPP 双栈并行扫描,功耗与系统负载较高;回连频繁失败时会形成连续扫描循环,建议业务侧在 onError 后主动 stopScan 并引导用户。
  • 广播数据解析:isReconnectDevice 对每个扫描到的设备执行 JL_OTA.toHexString(advertiseData) 与 indexOf 查找,属于 O(广播长度) 操作;源码中已用"仅命中模糊前缀才打印日志"来避免日志 IO 拖慢扫描回调。
  • 单例生命周期:bluetoothInstance 为模块级单例,init() 多次调用会重复注册事件回调(on 不判重),建议在页面入口只调用一次;应用退出时若需要释放资源,应调用各 impl 的 off 取消订阅(BluetoothOTAManager 当前未提供显式销毁方法)。

Extension Points

扩展点位置如何扩展
自定义回连onNeedReconnect 内 isInnerReconnect() == false 分支替换 ReconnectOp.isReconnectDevice 的匹配算法;onReconnectSuccess 后通过 reconnectCallback.onResult(newDeviceId) 交还 SDK
新回连广播识别isReconnectDevice修改魔数 "D60541544F4C4A" 与 MAC 提取偏移(当前为魔数后 6 字节反转),适配私有广播格式变化
回连协议栈扩展connectDevice 内 const isBle = true当设备支持 EDR 回连时改为按设备类型/固件能力判断,走 SppDevice 分支
升级数据源onReadData(offset, size)固件分片可来自文件、内存或网络,只需返回对应偏移的 Uint8Array
认证策略isUseAuth返回 false 可跳过 RCSP 认证(仅用于调试,正式固件应保持 true)

Tests

本目录(entry/src/main/ets/bluetooth/)未包含针对管理器的独立测试文件;管理器行为的验证依赖集成场景(连接设备 → 触发升级 → 观察 onProgress / onNeedReconnect 回调链)。源码注释中的调试日志(Log.i(TAG, "isReconnectDevice,mac:...")、"newReconnect"、"oldReconnect")可作为手动验证回连判定是否命中的观测点。

Related Links

  • 蓝牙设备基类与错误常量(base/BluetoothDevice、BluetoothErrorConstant)
  • BLE 实现层(BleImpl、BleDevice、连接/扫描配置)
  • SPP 实现层(SppImpl、SppDevice、Socket 配置)
  • OTA 封装层(OtaWrapper、OTAWrapperOption、Reconnect)
  • rcsp SDK 协议(JL_OTA)
Prev
SPP 模块实现