杰理 SDK 文档中心
首页
首页
  • 概览与快速开始

    • 项目概述与能力总览
    • 快速开始与 SDK 集成
  • SDK 核心接口

    • 发送接口 BleMethod
    • 接收接口 BleEventStream
    • 数据模型与常量定义
  • 平台原生实现

    • Android 原生层
    • iOS 原生层架构
    • iOS 蓝牙管理与 SDK 运行
    • 辅助连接与广播音箱
  • OTA 升级功能

    • 升级流程与传输通道
    • 自动回连机制
    • 复用空间升级
    • 自定义命令
  • 示例应用

    • 页面结构与用户旅程
    • 设备扫描与连接管理
    • 固件文件管理
    • 升级执行与状态展示
    • 设置与调试
  • 文档与支持

    • 接口文档与收发说明
    • 调试与问题排查

iOS 蓝牙管理与 SDK 运行

本页介绍 JL_OTA Flutter 工程在 iOS 平台上的蓝牙管理实现与杰理 SDK 运行机制,涵盖 JLBleHandler 双模式连接架构、JLBleManager/JL_BLEMultiple 扫描连接流程、Flutter 事件/方法通道桥接,以及 JL_BLEKit.framework 与 JL_OTALib.framework 两大底层 SDK 在 OTA 升级中的协作方式。

Purpose and Scope

本页聚焦于 iOS 原生侧「蓝牙管理与 SDK 运行」这一完整能力边界,包括:

  • BLE 连接管理:JLBleHandler 统一封装扫描、连接、断开、配对、状态查询,并支持「SDK 连接模式」与「自定义连接模式」双通道切换;
  • Flutter ↔ iOS 桥接:EventChannelHandler(事件流:设备发现、连接状态)与 MethodChannelHandler(方法调用:扫描、连接、OTA)如何协同;
  • 杰理 SDK 运行:JL_RunSDK、JL_BLEMultiple(RCSP 通信)、JL_OTAManager/JLOtaFlowProcessor(OTA 升级)、JLOtaReConnectMgr(OTA 断线重连)的职责与数据流;
  • 辅助连接组件:JLBleAssistManager 等周边实现。

以下主题属于兄弟页面,不在本页展开:Android 平台蓝牙实现(见「Android 蓝牙管理与 SDK 运行」)、OTA 文件格式与固件包解析(见「OTA 升级与固件管理」)、Flutter 侧 UI 状态管理(见「Flutter 层设备连接与 OTA 流程」)。

Overview

JL_OTA_Flutter 是一个跨平台 OTA 升级应用,iOS 侧所有与蓝牙相关的原生能力都围绕「一个统一入口 + 两套底层实现」展开:

  • 统一入口:JLBleHandler(单例)对外暴露与连接方式无关的接口(isConnected、handleScanDevice、handleStopScanDevice、handleGetBleStatus、handleDeviceNowUUID、getCurrentOtaStatus 等),上层 Flutter 代码不需要关心底层用的是哪套蓝牙栈;
  • 两套底层实现:
    1. SDK 连接模式:使用杰理官方 JL_BLEKit.framework 中的 JL_BLEMultiple(CoreBluetooth 中央管理器封装)与 JL_RunSDK 运行时;所有 RCSP 指令、配对鉴权、OTA 均由 SDK 内部完成,App 通过 delegate 回调感知结果;
    2. 自定义连接模式:使用工程自研的 JLBleManager 直接操作 CoreBluetooth,OtaManager 等协议层由工程侧管理;
  • 切换开关:ToolsHelper.isConnectBySDK() 决定当前走哪套实现,EventChannelHandler 依据该开关分发不同通知(kFLT_BLE_* 自定义通知 vs kJL_BLE_M_* SDK 通知)。

选择这套双模式设计的原因:杰理 SDK 封装了完整、稳定的 RCSP 协议(含鉴权、批量指令、OTA 分包),可显著降低接入成本;但部分客户需要深度定制连接行为(如私有过滤、特殊配对流程),因此保留自定义模式作为降级与定制通道。两种模式共用同一套 Flutter 事件协议,上层无感知。

Architecture

下图展示了 iOS 侧蓝牙管理与 SDK 运行的整体分层与依赖关系:

flowchart TD
    subgraph sg_Flutter["Flutter 层"]
        UI["Flutter UI / 业务层"]
        MC["MethodChannel<br/>(方法调用)"]
        EC["EventChannel<br/>(事件流)"]
    end

    subgraph sg_Bridge["iOS 桥接层"]
        MCH["MethodChannelHandler.swift"]
        ECH["EventChannelHandler.swift<br/>FlutterStreamHandler"]
        JLH["JLBleHandler.m<br/>(统一入口/单例)"]
        TH["ToolsHelper<br/>(isConnectBySDK 开关)"]
    end

    subgraph sg_SDKMode["SDK 连接模式 (JL_BLEKit.framework)"]
        RUN["JL_RunSDK"]
        BLEM["JL_BLEMultiple<br/>CBCentralManagerDelegate"]
        ENT["JL_EntityM<br/>(设备实体/RCSP)"]
        OTA["JL_OTAManager<br/>+ JLOtaFlowProcessor"]
        RECON["JLOtaReConnectMgr<br/>(OTA 断线重连)"]
    end

    subgraph sg_CustomMode["自定义连接模式"]
        CUST["JLBleManager"]
        ASSIST["JLBleAssistManager"]
        CUSTOTA["JLBleManager.otaManager"]
    end

    subgraph sg_HW["系统层"]
        CB[(CoreBluetooth<br/>CBCentralManager / CBPeripheral)]
        DEV["杰理蓝牙设备<br/>(耳机/音箱/手表等)"]
    end

    UI --> MC
    UI --> EC
    MC --> MCH
    EC --> ECH
    MCH --> JLH
    ECH --> JLH
    TH --> JLH
    JLH -->|"isConnectBySDK = YES"| RUN
    RUN --> BLEM
    BLEM --> ENT
    ENT --> OTA
    OTA --> RECON
    JLH -->|"isConnectBySDK = NO"| CUST
    CUST --> ASSIST
    CUST --> CUSTOTA
    BLEM --> CB
    CUST --> CB
    ASSIST --> CB
    CB -.->|"BLE 广播/连接"| DEV

架构解读:

  • 桥接层是唯一面向 Flutter 的边界:MethodChannelHandler 处理 Dart 侧发来的命令,EventChannelHandler 将蓝牙事件持续推送给 Dart;两者都委托给 JLBleHandler 执行实际动作。
  • JLBleHandler 是双模式路由器:其内部同时持有 sdkManager(JL_BLEMultiple 指针,来自 JL_RunSDK)与 userManager(JLBleManager 单例),每个对外方法先用 ToolsHelper.isConnectBySDK() 判断路由目标(见 JLBleHandler.m)。
  • SDK 模式自成闭环:JL_BLEMultiple 是 CoreBluetooth 中央管理器的封装(二进制 SDK 内实现 centralManager:didConnectPeripheral: 等回调),JL_EntityM 承载 RCSP 通信状态(mIsAuth、mCmdManager),OTA 由 JL_OTAManager 驱动 JLOtaFlowProcessor 状态机,断线时 JLOtaReConnectMgr 依据 MAC 或 UUID 重连。
  • 自定义模式是轻量降级通道:JLBleManager 直接管理 CBCentralManager/CBPeripheral,配网配对与 OTA 由 otaManager 承担;JLBleAssistManager 提供辅助连接上下文(持有 mBleManagerState、mBlePeripheral,见 JLBleAssistManager.h)。

核心实现:JLBleHandler 双模式管理器

JLBleHandler 是 iOS 蓝牙管理的门面(Facade),采用单例模式,职责是屏蔽两种底层实现的差异,为桥接层提供稳定的操作接口。

单例与初始化

+ (instancetype)share {
    static JLBleHandler *handler;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        handler = [[JLBleHandler alloc] init];
    });
    return handler;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        [self setupManagers];
    }
    return self;
}

- (void)setupManagers {
    sdkManager = [JL_RunSDK sharedInstance].mBleMultiple;
    [JL_RunSDK sharedInstance].otaDelegate = self;
    sdkManager.BLE_FILTER_ENABLE = YES;

    userManager = [JLBleManager sharedInstance];
    [userManager addDelegate:self];
}

Source: JLBleHandler.m

设计要点:

  • dispatch_once 单例保证全工程只有一个 BLE 管理入口,避免 CoreBluetooth 多实例竞争(同一 CBCentralManager 不应被多处创建);
  • 双管理器同时初始化:setupManagers 既从 JL_RunSDK 取出 SDK 模式的 sdkManager(JL_BLEMultiple),也初始化自定义模式的 userManager(JLBleManager),二者可随时切换;
  • BLE_FILTER_ENABLE = YES 表示 SDK 模式启用设备过滤(仅接受杰理自家广播格式的设备),防止扫描到无关 BLE 设备;
  • JLBleHandler 同时遵循 JLBleManagerOtaDelegate 与 JL_RunSDK OtaDelegate 两个协议,因此无论是 SDK 模式还是自定义模式的 OTA 回调,都会汇聚到同一个处理器。

双模式路由核心方法

所有对外方法都遵循同一个「先判断模式、再路由调用」的模式:

- (void)handleScanDevice {
    if ([ToolsHelper isConnectBySDK]) {
        [sdkManager scanStart];
    } else {
        [userManager startScanBLE];
    }
}

- (BOOL)handleGetBleStatus {
    BOOL isPoweredOn = NO;
    if ([ToolsHelper isConnectBySDK]) {
        isPoweredOn = (sdkManager.bleManagerState == CBManagerStatePoweredOn);
    } else {
        isPoweredOn = ([JLBleManager sharedInstance].mBleManagerState == CBManagerStatePoweredOn);
    }
    return isPoweredOn;
}

- (NSString *)handleDeviceNowUUID {
    if ([ToolsHelper isConnectBySDK]) {
        return [JL_RunSDK sharedInstance].mBleEntityM.mPeripheral.identifier.UUIDString;
    } else {
        return [JLBleManager sharedInstance].mBlePeripheral.identifier.UUIDString;
    }
}

Source: JLBleHandler.m

路由语义:

方法SDK 模式动作自定义模式动作说明
handleScanDevicesdkManager scanStartuserManager startScanBLE启动扫描
handleStopScanDevicesdkManager scanStopuserManager stopScanBLE停止扫描
handleGetBleStatussdkManager.bleManagerState == PoweredOnJLBleManager.mBleManagerState查询蓝牙是否可用
isConnectedJL_EntityM.mIsAuth(鉴权通过才算连接)JLBleManager isConnected连接判定(SDK 模式要求完成 RCSP 鉴权)
handleDeviceNowUUIDJL_EntityM.mPeripheral.identifier.UUIDStringJLBleManager.mBlePeripheral...当前设备 UUID
getCurrentOtaStatusmCmdManager outputDeviceModel].otaStatususerManager.otaManager.otaStatusOTA 状态(普通/强制)

设备类型识别

JLBleHandler.m 顶部定义了设备类型常量,用于在 OTA 回调中区分不同产品形态:

static NSString * const kDeviceTypeSoundBox = @"sound box";
static NSString * const kDeviceTypeChargingBin = @"charging box";
static NSString * const kDeviceTypeTWS = @"TWS";
static NSString * const kDeviceTypeHeadset = @"headset";
static NSString * const kDeviceTypeSoundCard = @"sound card";
static NSString * const kDeviceTypeWatch = @"watch";
static NSString * const kDeviceTypeTradition = @"tradition";
static NSString * const kDeviceTypeUnknown = @"unKnow";

Source: JLBleHandler.m

音箱(sound box)、充电仓(charging box)、TWS、耳机(headset)、声卡(sound card)、手表(watch)等设备在 OTA 流程中会走不同的升级路径(例如 TWS 需要左右耳分别升级、手表需要表盘资源管理),类型常量让上层能针对性地展示 UI 与执行升级策略。

Flutter 桥接:事件通道与方法通道

EventChannelHandler —— 事件流推送

EventChannelHandler 实现 FlutterStreamHandler,负责把蓝牙扫描与连接事件持续推送到 Dart 侧。它的核心机制是监听 8 个 NSNotification,再按连接模式分发:

private func setupNotificationObservers() {
    let notificationNames: [Notification.Name] = [
        Notification.Name(kFLT_BLE_FOUND),
        Notification.Name(kFLT_BLE_CONNECTED),
        Notification.Name(kFLT_BLE_DISCONNECTED),
        Notification.Name(kFLT_BLE_PAIRED),
        Notification.Name(kJL_BLE_M_FOUND),
        Notification.Name(kJL_BLE_M_ENTITY_CONNECTED),
        Notification.Name(kJL_BLE_M_ENTITY_DISCONNECTED),
        Notification.Name(kJL_BLE_M_OFF)
    ]
    for name in notificationNames {
        NotificationCenter.default.addObserver(self, selector: #selector(self.handleAllNotifications(_:)), name: name, object: nil)
    }
}

@objc private func handleAllNotifications(_ notification: Notification) {
    let name = notification.name.rawValue
    if !ToolsHelper.isConnectBySDK() {
        // 自定义连接模式
        if name == kFLT_BLE_FOUND {
            handleCustomModeDeviceFound(notification)
        } else if name == kFLT_BLE_CONNECTED || name == kFLT_BLE_DISCONNECTED || name == kJL_BLE_M_OFF {
            handleCustomModeConnectionChange(name)
        } else if name == kFLT_BLE_PAIRED {
            handleCustomModePaired(notification)
        }
    } else {
        // SDK 连接模式
        if name == kJL_BLE_M_FOUND {
            handleSDKModeDeviceFound(notification)
        } else if name == kJL_BLE_M_ENTITY_CONNECTED {
            handleSDKModeConnected(notification)
        } else if name == kJL_BLE_M_ENTITY_DISCONNECTED || name == kJL_BLE_M_OFF {
            handleSDKModeDisconnected()
        }
    }
    ...
}

Source: EventChannelHandler.swift

通知命名空间说明:

  • kFLT_BLE_* 前缀:自定义连接模式(JLBleManager)发出的通知,覆盖设备发现、连接、断开、配对完成;
  • kJL_BLE_M_* 前缀:杰理 SDK 模式发出的通知(JL_BLEMultiple/JL_EntityM 产生),覆盖发现、实体连接/断开、蓝牙关闭(kJL_BLE_M_OFF)。

事件通道还定义了供 Dart 消费的状态枚举:

enum ScanState {
    case scanning
    case foundDevice
    case idle
}

enum ConnectionState: Int {
    case disconnected = 0
    case connected = 1
    case failed = 2
    case connecting = 3
}

Source: EventChannelHandler.swift

线程安全设计:事件处理器内部使用 serialQueue = DispatchQueue(label: "com.eventchannel.serial") 串行队列同步处理设备列表变更(如 serialQueue.sync { processCustomModeDeviceFound(...) }),避免扫描回调并发修改 btEnityList 造成数据竞争;同时用 pendingWorkItems 跟踪异步 DispatchWorkItem 以便在页面销毁时清理(例如 0.5 秒的退出延时与 30 秒的长期延时任务)。

MethodChannelHandler —— 命令下发

MethodChannelHandler 接收 Dart 侧的方法调用,典型流程是:收到「连接设备」请求后,先根据工程配置决定是否要求配对,再调用 JLBleManager 连接:

JLBleManager.sharedInstance().isPaired = ToolsHelper.isSupportPair()
JLBleManager.sharedInstance().connectBLE(peripheral)

Source: MethodChannelHandler.swift

ToolsHelper.isSupportPair() 决定设备是否需要走配对流程;EventChannelHandler 中维护当前实体:

guard let currentEntity = JLBleManager.sharedInstance().currentEntity else { return }
...
JLBleManager.sharedInstance().currentEntity = nil
sendEvent(EventChannelConstants.TYPE_DEVICE_CONNECTION, data: [...])

Source: EventChannelHandler.swift

currentEntity 是自定义模式下的「当前设备」模型;断开时先置空再向 Flutter 发送 TYPE_DEVICE_CONNECTION 事件,保证 Dart 侧状态与原生一致(先改状态、后发通知,避免竞态窗口)。

核心流程:从扫描到 OTA 升级

整体时序

下面的时序图展示 SDK 连接模式下,从 Flutter 发起扫描到 OTA 升级完成的全链路:

sequenceDiagram
    participant F as Flutter UI
    participant M as MethodChannelHandler
    participant H as JLBleHandler
    participant S as JL_BLEMultiple (SDK)
    participant E as JL_EntityM (RCSP)
    participant O as JL_OTAManager / JLOtaFlowProcessor
    participant D as 杰理蓝牙设备
    participant EC as EventChannelHandler

    F->>M: 调用 startScan
    M->>H: handleScanDevice()
    H->>S: scanStart()
    S->>D: CoreBluetooth 扫描广播
    D-->>S: 广播包 (含 MAC 厂商数据)
    S-->>H: 发现设备回调
    H-->>EC: 发送 kJL_BLE_M_FOUND 通知
    EC-->>F: 事件流推送设备列表

    F->>M: 调用 connect(uuid/mac)
    M->>H: handleConnectDevice()
    H->>S: connectEntity:Result:
    S->>D: 建立 GATT 连接
    S->>E: 发现 RCSP Service/Characteristic
    E->>D: RCSP 鉴权 (mIsAuth)
    D-->>E: 鉴权通过
    E-->>H: noteEntityConnected
    H-->>EC: kJL_BLE_M_ENTITY_CONNECTED 通知
    EC-->>F: TYPE_DEVICE_CONNECTION = connected

    F->>M: 调用 startOTA(file)
    M->>H: handleStartOta()
    H->>O: cmdUpgrade:Option:Result:
    O->>E: OTA_2 AskForUpgrade
    E-->>O: 可升级确认
    O->>O: 分包传输 cmdUpdateBuffer / otaDataSend
    O->>D: 固件数据包 (按 MTU 分包)
    D-->>O: 传输进度响应
    O-->>H: otaFlowDidUpdateStatus:progress:
    H-->>F: OTA 进度回调 (kFLT_BLE_OTA_CALLBACK)
    O->>O: OTA_7 RebootDevice
    O->>D: 重启进入新固件
    D-->>O: 断开连接
    O->>RECON: (如启用) JLOtaReConnectMgr 重连

关键状态流:连接生命周期

stateDiagram-v2
    [*] --> Idle: 蓝牙关闭/未扫描
    Idle --> Scanning: handleScanDevice()
    Scanning --> Discovered: 发现设备 (kJL_BLE_M_FOUND)
    Discovered --> Connecting: 用户选择设备
    Connecting --> Authorizing: GATT 连接成功
    Authorizing --> Connected: RCSP 鉴权通过 (mIsAuth = YES)
    Connecting --> Failed: didFailToConnect / 超时
    Authorizing --> Failed: 鉴权失败
    Connected --> OTAUpgrading: startOTA
    OTAUpgrading --> Connected: 升级完成并重连
    Connected --> Idle: 断开 (kJL_BLE_M_ENTITY_DISCONNECTED / kJL_BLE_M_OFF)
    Failed --> Idle: 清理状态

状态语义(SDK 模式):

  • Authorizing:GATT 连接成功后,SDK 会自动向设备发起 RCSP 鉴权(配对密钥交换);isConnected 返回 mIsAuth,即鉴权通过才算真正「已连接」——这是杰理 SDK 与普通 BLE 连接的重要区别,因为 RCSP 通道建立后指令才可用;
  • OTAUpgrading:升级期间若连接断开,JLOtaReConnectMgr 会按配置以 MAC 或 UUID 重新扫描并连接设备(日志关键字 kLogTagReconnectByMac/kLogTagReconnectByUUID,见 JLBleHandler.m),JL_OTAManager 通过 setOTARelink: 获知重连状态。

SDK 运行机制:JL_BLEKit 与 JL_OTALib

框架职责划分

框架核心类职责
JL_BLEKit.frameworkJL_BLEMultipleCoreBluetooth 中央管理器封装:扫描、连接、广播解析、MAC 提取(otaBleMacAddressFromCBAdvDataManufacturerData:);实现 CBCentralManagerDelegate 全部回调(centralManagerDidUpdateState:、didDiscoverPeripheral:...、didConnectPeripheral:、didDisconnectPeripheral:error: 等)
JL_BLEKit.frameworkJL_EntityM设备实体:持有 mPeripheral、RCSP 读写特征(mRcspWrite/mRcspRead)、mIsAuth 鉴权标志、mCmdManager 指令管理器
JL_BLEKit.frameworkJL_RunSDK运行时门面:提供 mBleMultiple、mBleEntityM、otaDelegate,App 侧入口
JL_OTALib.frameworkJL_OTAManagerOTA 总控:单例 getOTAManager,管理 JLOtaFlowProcessor 与 JLOtaDataHandler,暴露 cmdUpgrade:Option:Result:、cmdOTACancelResult:、resetOTAManager、setOTARelink:、maxLostCount:、cmdTimeOut: 等
JL_OTALib.frameworkJLOtaFlowProcessorOTA 状态机:otaFirstStep:(单分区)→ otaPartitionSingle:/otaPartitionDouble → otaSecondStep → cmdUpdateBuffer:(分包传输)→ cmdOTA_7_RebootDevice;处理 handleCommand:sn:payload: / handleResponse:sn:payload: / handleTimeout:
JL_OTALib.frameworkJLOtaDataHandlerRCSP 数据包编解码:xmCommandCode:needRep:data:UUID:、parseInputPackage:、CRC16 附加(addCrc:)
JL_OTALib.frameworkJLOtaTimeoutManager指令超时管理:startTimeOut:OpCode:、cancelTimeOut:OpCode:,超时回调 handleTimeout:
JL_OTALib.frameworkJLOtaReConnectMgrOTA 断线重连:实现 CBCentralManagerDelegate/CBPeripheralDelegate,reconnectWithMac:Result: / reconnectWithUUID:Result:
JL_OTALib.frameworkJLOTAFile固件文件下载:cmdGetOtaFileKey:Code:Result:、downloadOtaFileWithUrl:(通过 NSURLSession 拉取固件,校验 auth_key/proj_code)

上述类名与方法签名提取自 JL_BLEKit.framework/JL_BLEKit 与 JL_OTALib.framework/JL_OTALib 的符号表(二进制 SDK,无源码)。

OTA 升级的状态机

JLOtaFlowProcessor 的升级流程按设备固件分区形态分支:

flowchart TD
    Start([startOTAWithData]) --> Ask["cmdOTA_2 AskForUpgrade<br/>(检查可否升级)"]
    Ask -->|"不可升级"| Fail([升级失败回调])
    Ask -->|"可升级"| Mark["cmdOTA_1 GetMarkSeek<br/>(读取固件标记)"]
    Mark --> Status{"otaStatus 判定"}
    Status -->|"JL_OtaStatusNormal"| Normal["正常升级流程"]
    Status -->|"JL_OtaStatusForce"| Force["强制升级流程<br/>handleWeatherNeedUpdate = YES"]
    Normal --> Partition{"分区形态"}
    Partition -->|"JL_PartitionSingle"| Single["otaPartitionSingle:<br/>单分区直写"]
    Partition -->|"JL_PartitionDouble"| Double["otaPartitionDouble:<br/>双分区切换"]
    Single --> Buffer["cmdUpdateBuffer:<br/>按 MTU 分包传输固件"]
    Double --> Buffer
    Buffer -->|"传输完成"| Reboot["cmdOTA_7 RebootDevice"]
    Reboot --> Done([升级完成 / 断开重连])
    Force --> Reboot

设计意图:

  • 分区判断(JL_PartitionSingle vs JL_PartitionDouble)来自设备信息解析(JLOtaDeviceInfoParser parseInfo:intoManager:),单分区设备需要一次性写入,双分区设备可写备份分区、失败后回滚,这是杰理 OTA 容错设计的核心;
  • 超时与丢包:JL_OTAManager 提供 maxLostCount:(最大丢包计数)与 cmdTimeOut:(指令超时),JLOtaTimeoutManager 对每条 RCSP 指令启动超时定时器,超时触发 handleTimeout: 让上层决定重发或终止;
  • OTA 结束后重连:升级完成后设备重启,updateRelinkType: 根据 JL_OTAResultReconnectWithMacAddr / JL_OTAResultReconnectWithUUid 选择重连方式,JLOtaReConnectMgr 用独立 CBCentralManager 扫描目标 MAC 或 UUID 完成重连——因为此时旧连接已因设备重启而失效。

配置选项

iOS 侧蓝牙管理的行为主要由 ToolsHelper 的工程开关与 SDK 属性控制:

配置项类型默认值说明
ToolsHelper.isConnectBySDK()BOOL工程宏决定蓝牙连接模式开关:YES 走杰理 SDK(JL_RunSDK/JL_BLEMultiple),NO 走自定义 JLBleManager
ToolsHelper.isSupportPair()BOOL工程宏决定设备是否需要配对流程,赋值给 JLBleManager.isPaired 后连接时执行配对
sdkManager.BLE_FILTER_ENABLEBOOLYES(setupManagers 中设置)SDK 扫描过滤:仅接受杰理广播格式设备(JLBleHandler.m)
sdkManager.BLE_TIMEOUTNSIntegerSDK 默认连接超时阈值
JL_OTAManager.maxLostCount:intSDK 默认OTA 传输最大丢包计数,超过后判定链路异常
JL_OTAManager.cmdTimeOut:intSDK 默认RCSP 指令超时秒数
JL_OTAManager.setOTARelink:BOOLSDK 默认是否启用 OTA 断线自动重连
EventChannelHandler.DELAY_EXIT_TIMEDouble0.5 秒页面退出时的延时清理时间(EventChannelHandler.swift)
EventChannelHandler.DELAY_EXIT_LONG_TIMEDouble30.0 秒长任务(如等待重连)的退出保护时间
JLOtaReConnectOption对象defaultOptionOTA 重连参数:serviceUUID/writeUUID/readUUID/authKey/deviceAuthorize/isWriteWithResponse

注意:BLE_FILTER_ENABLE、BLE_TIMEOUT、maxLostCount 等 SDK 属性定义在 JL_BLEKit.framework 二进制符号中(见 JL_BLEKit 符号表),具体默认值以杰理 SDK 文档为准。

API 参考

JLBleHandler(统一入口,单例)

方法说明
+ (instancetype)share获取全局单例
- (void)setDelegate:(id<JLBleHandlDelegate>)delegate注册回调代理,同时把 JL_RunSDK.otaDelegate 指向自身
- (BOOL)isConnected是否已连接(SDK 模式以 JL_EntityM.mIsAuth 为准)
- (BOOL)handleGetBleStatus蓝牙是否已开启(CBManagerStatePoweredOn)
- (void)handleScanDevice开始扫描
- (void)handleStopScanDevice停止扫描
- (NSString *)handleDeviceNowUUID当前连接设备的 UUID 字符串
- (JL_OtaStatus)getCurrentOtaStatus当前 OTA 状态(Normal/Force)
- (BOOL)handleWeatherNeedUpdate是否需要「强制天气升级」(otaStatus == JL_OtaStatusForce)

EventChannelHandler(事件流)

方法说明
func sendScanDeviceListToFlutter()蓝牙开启时向 Flutter 推送扫描中的设备列表(scanState: .scanning)
func handleAllNotifications(_:)统一通知入口:按 isConnectBySDK 分派到自定义/SDK 两套处理逻辑
func ensureCurrentDeviceAtTop()保证当前连接设备排在设备列表首位
enum ScanStatescanning / foundDevice / idle
enum ConnectionState: Intdisconnected=0 / connected=1 / failed=2 / connecting=3

MethodChannelHandler(方法通道)

方法说明
JLBleManager.sharedInstance().isPaired = ToolsHelper.isSupportPair()设置是否要求配对后调用连接
JLBleManager.sharedInstance().connectBLE(peripheral)发起自定义模式连接

JL_OTAManager(OTA 总控,来自 JL_OTALib.framework)

方法说明
+ (JL_OTAManager *)getOTAManager单例获取
- (void)cmdUpgrade:(JL_OtaStatus)status Option:(... ) Result:(...)启动升级(按 otaStatus 与分区进入不同流程)
- (void)cmdOTACancelResult:(...)取消升级
- (void)resetOTAManager重置 OTA 管理器(升级失败/重连前清理状态)
- (void)setOTARelink:(BOOL)isRelink启用/关闭断线自动重连
- (void)maxLostCount:(int)count设置最大丢包计数
- (void)cmdTimeOut:(int)time设置指令超时
- (void)intputDeviceName:(NSString *)name注入设备名用于日志/重连匹配

签名细节以杰理 SDK 头文件为准;此处列出的是符号表中可验证的公开接口(JL_OTALib 符号表)。

失败模式、边界情况与并发

失败模式

场景表现处理方式
蓝牙未开启handleGetBleStatus 返回 NO,扫描不启动桥接层直接返回,Flutter 展示引导开启蓝牙;kJL_BLE_M_OFF 通知会触发 handleSDKModeDisconnected/handleCustomModeConnectionChange 清理连接状态
蓝牙权限拒绝/不支持CBManagerStateUnauthorized/UnsupportedSDK centralManagerDidUpdateState: 打日志(BLE--> CBManagerStateUnsupported 等),上层应提示权限设置
连接失败didFailToConnectPeripheral:error:SDK 日志 Err:BLE Connect FAIL,JLBleHandler 将失败状态回传 Flutter(ConnectionState.failed)
鉴权失败mIsAuth 未置 YESisConnected 为 NO;SDK 回调 noteEntityPairFail:/noteEntityPaired: 驱动配对流程
OTA 指令超时JLOtaTimeoutManager 触发 handleTimeout:按指令重发或终止升级,cmdTimeOut: 可调
OTA 传输丢包过多maxLostCount: 超限判定链路异常,终止并提示重试;升级中断线由 JLOtaReConnectMgr 自动重连
升级后设备重启断连didDisconnectPeripheral:error:updateRelinkType: 按 MAC/UUID 重连(JL_OTAResultReconnectWithMacAddr / ...UUid)
强制升级失败otaStatus == JL_OtaStatusForcehandleWeatherNeedUpdate 返回 YES,上层限制其它功能、引导用户完成升级

边界情况

  • 当前设备置顶:ensureCurrentDeviceAtTop() 依赖 JLBleManager.currentEntity;当 currentEntity 为 nil(尚未连接)时直接 return,避免数组越界;
  • 退出时序:EventChannelHandler 用 0.5 秒延时清理 DispatchWorkItem,30 秒保护长任务,防止页面销毁后事件回调崩溃;
  • 设备类型差异:声卡/手表/TWS 等类型常量决定 OTA 路径差异,未知类型回落 kDeviceTypeUnknown。

并发与线程安全

  • 串行队列:EventChannelHandler 用 DispatchQueue(label: "com.eventchannel.serial") 包裹设备列表变更(serialQueue.sync),保证多通知并发时的 btEnityList 一致性;
  • 通知顺序:断开处理「先置空 currentEntity、再发事件」,消除 Dart 侧读到陈旧状态的窗口;
  • 单例约束:JLBleHandler 与 JL_OTAManager 均为 dispatch_once 单例,避免多个 CBCentralManager 实例互相干扰;
  • 先改状态后通知:连接状态变更遵循「状态更新 → 通知发布」顺序,降低竞态。

性能与运维注意事项

  • 扫描功耗:handleScanDevice 启动后应尽快由 UI 层调用 handleStopScanDevice 停止扫描;SDK 过滤(BLE_FILTER_ENABLE = YES)可减少无关广播带来的 CPU 开销;
  • MTU 分包:OTA 固件传输按 MTU 分包(maximumWriteValueLengthForType: 决定单包大小),JLOtaDataHandler 负责分包/重组与 CRC16 校验;MTU 越大吞吐越高,但受设备能力限制;
  • 日志可观测性:SDK 内置 JLLogManager 日志体系(logLevel:funcName:line:format:),OTA 侧有 logOtaSendData、JL_OTAManager logSendData: 与 logSDKVersion,可用于定位「发送-响应-超时」链路问题;
  • 强制升级优先级:JL_OtaStatusForce 时 handleWeatherNeedUpdate 返回 YES,业务上应阻塞其它操作、优先完成升级,避免设备处于不稳定固件状态;
  • 固件下载:JLOTAFile 通过 NSURLSession 从 profile.jieliapp.com/license/v1/fileupdate/check 拉取固件(带 auth_key/proj_code/hash 校验),网络异常时 OTA 未开始即失败,需提示用户检查网络。

扩展点

  1. 新增连接模式:JLBleHandler 的每个方法都是「模式路由」模板,新增第三种连接栈只需在 ToolsHelper.isConnectBySDK 之外增加分支,并在 EventChannelHandler.handleAllNotifications 增加对应通知命名空间;
  2. 自定义 OTA 指令:JL_OTALib.framework 提供 JLOtaCustom(initWithDelegate:OtaManager:、cmdSendCommandData:needResponse:Result:),可在标准 OTA 流程之外透传自定义 RCSP 指令(如产测指令);
  3. 重连策略定制:JLOtaReConnectOption(serviceUUID/writeUUID/readUUID/authKey/deviceAuthorize/isWriteWithResponse)允许按项目定制 OTA 重连的 GATT 服务与鉴权参数,支持 defaultOption 一键恢复;
  4. 设备类型扩展:kDeviceType* 常量集合可扩展新产品形态,配合 JLOtaDeviceInfoParser parseInfo:intoManager: 解析出的分区/状态信息驱动差异化升级 UI;
  5. 事件协议扩展:EventChannelConstants.TYPE_DEVICE_CONNECTION 等事件常量是 Flutter 与原生约定的协议,新增业务事件(如电量上报、设备功能列表)时在 EventChannelHandler 中追加通知映射即可。

测试与验证

仓库在 code/JL_OTA/example/ios/ 下提供 iOS 示例工程,集成 JL_BLEKit.framework 与 JL_OTALib.framework,可用真实杰理设备验证以下路径:

  • 蓝牙开关状态与权限回调(centralManagerDidUpdateState: 各状态日志);
  • 扫描过滤(仅显示 BLE_FILTER_ENABLE 接受的设备)与 MAC 提取(otaBleMacAddressFromCBAdvDataManufacturerData:);
  • 连接 + RCSP 鉴权(mIsAuth 置位)与 kFLT_BLE_PAIRED/kJL_BLE_M_ENTITY_CONNECTED 通知;
  • OTA 升级:单分区/双分区、普通/强制状态、传输进度、断线重连(MAC/UUID 两种)、超时与丢包恢复;
  • 双模式切换:通过 ToolsHelper.isConnectBySDK 宏切换后复测上述链路,确认 EventChannelHandler 通知分发正确。

二进制 SDK 的完整行为需结合杰理官方 SDK 文档;本页所有类名、方法名与流程均来自仓库内 framework 符号表与原生源码的交叉验证。

Related Links

  • JLBleHandler.m(统一入口源码)
  • EventChannelHandler.swift(事件通道源码)
  • MethodChannelHandler.swift(方法通道源码)
  • JLBleAssistManager.h(辅助连接管理器)
  • JL_BLEKit.framework(RCSP 协议与 BLE 封装 SDK)
  • JL_OTALib.framework(OTA 升级 SDK)
  • 相关页面:Android 蓝牙管理与 SDK 运行(3-native-implementation/3-2-android-ble-manager)、OTA 升级与固件管理、Flutter 层设备连接与 OTA 流程
Prev
iOS 原生层架构
Next
辅助连接与广播音箱