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 代码不需要关心底层用的是哪套蓝牙栈; - 两套底层实现:
- SDK 连接模式:使用杰理官方
JL_BLEKit.framework中的JL_BLEMultiple(CoreBluetooth 中央管理器封装)与JL_RunSDK运行时;所有 RCSP 指令、配对鉴权、OTA 均由 SDK 内部完成,App 通过 delegate 回调感知结果; - 自定义连接模式:使用工程自研的
JLBleManager直接操作 CoreBluetooth,OtaManager 等协议层由工程侧管理;
- SDK 连接模式:使用杰理官方
- 切换开关:
ToolsHelper.isConnectBySDK()决定当前走哪套实现,EventChannelHandler依据该开关分发不同通知(kFLT_BLE_*自定义通知 vskJL_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 模式动作 | 自定义模式动作 | 说明 |
|---|---|---|---|
handleScanDevice | sdkManager scanStart | userManager startScanBLE | 启动扫描 |
handleStopScanDevice | sdkManager scanStop | userManager stopScanBLE | 停止扫描 |
handleGetBleStatus | sdkManager.bleManagerState == PoweredOn | JLBleManager.mBleManagerState | 查询蓝牙是否可用 |
isConnected | JL_EntityM.mIsAuth(鉴权通过才算连接) | JLBleManager isConnected | 连接判定(SDK 模式要求完成 RCSP 鉴权) |
handleDeviceNowUUID | JL_EntityM.mPeripheral.identifier.UUIDString | JLBleManager.mBlePeripheral... | 当前设备 UUID |
getCurrentOtaStatus | mCmdManager outputDeviceModel].otaStatus | userManager.otaManager.otaStatus | OTA 状态(普通/强制) |
设备类型识别
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.framework | JL_BLEMultiple | CoreBluetooth 中央管理器封装:扫描、连接、广播解析、MAC 提取(otaBleMacAddressFromCBAdvDataManufacturerData:);实现 CBCentralManagerDelegate 全部回调(centralManagerDidUpdateState:、didDiscoverPeripheral:...、didConnectPeripheral:、didDisconnectPeripheral:error: 等) |
JL_BLEKit.framework | JL_EntityM | 设备实体:持有 mPeripheral、RCSP 读写特征(mRcspWrite/mRcspRead)、mIsAuth 鉴权标志、mCmdManager 指令管理器 |
JL_BLEKit.framework | JL_RunSDK | 运行时门面:提供 mBleMultiple、mBleEntityM、otaDelegate,App 侧入口 |
JL_OTALib.framework | JL_OTAManager | OTA 总控:单例 getOTAManager,管理 JLOtaFlowProcessor 与 JLOtaDataHandler,暴露 cmdUpgrade:Option:Result:、cmdOTACancelResult:、resetOTAManager、setOTARelink:、maxLostCount:、cmdTimeOut: 等 |
JL_OTALib.framework | JLOtaFlowProcessor | OTA 状态机:otaFirstStep:(单分区)→ otaPartitionSingle:/otaPartitionDouble → otaSecondStep → cmdUpdateBuffer:(分包传输)→ cmdOTA_7_RebootDevice;处理 handleCommand:sn:payload: / handleResponse:sn:payload: / handleTimeout: |
JL_OTALib.framework | JLOtaDataHandler | RCSP 数据包编解码:xmCommandCode:needRep:data:UUID:、parseInputPackage:、CRC16 附加(addCrc:) |
JL_OTALib.framework | JLOtaTimeoutManager | 指令超时管理:startTimeOut:OpCode:、cancelTimeOut:OpCode:,超时回调 handleTimeout: |
JL_OTALib.framework | JLOtaReConnectMgr | OTA 断线重连:实现 CBCentralManagerDelegate/CBPeripheralDelegate,reconnectWithMac:Result: / reconnectWithUUID:Result: |
JL_OTALib.framework | JLOTAFile | 固件文件下载: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_PartitionSinglevsJL_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_ENABLE | BOOL | YES(setupManagers 中设置) | SDK 扫描过滤:仅接受杰理广播格式设备(JLBleHandler.m) |
sdkManager.BLE_TIMEOUT | NSInteger | SDK 默认 | 连接超时阈值 |
JL_OTAManager.maxLostCount: | int | SDK 默认 | OTA 传输最大丢包计数,超过后判定链路异常 |
JL_OTAManager.cmdTimeOut: | int | SDK 默认 | RCSP 指令超时秒数 |
JL_OTAManager.setOTARelink: | BOOL | SDK 默认 | 是否启用 OTA 断线自动重连 |
EventChannelHandler.DELAY_EXIT_TIME | Double | 0.5 秒 | 页面退出时的延时清理时间(EventChannelHandler.swift) |
EventChannelHandler.DELAY_EXIT_LONG_TIME | Double | 30.0 秒 | 长任务(如等待重连)的退出保护时间 |
JLOtaReConnectOption | 对象 | defaultOption | OTA 重连参数: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 ScanState | scanning / foundDevice / idle |
enum ConnectionState: Int | disconnected=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/Unsupported | SDK centralManagerDidUpdateState: 打日志(BLE--> CBManagerStateUnsupported 等),上层应提示权限设置 |
| 连接失败 | didFailToConnectPeripheral:error: | SDK 日志 Err:BLE Connect FAIL,JLBleHandler 将失败状态回传 Flutter(ConnectionState.failed) |
| 鉴权失败 | mIsAuth 未置 YES | isConnected 为 NO;SDK 回调 noteEntityPairFail:/noteEntityPaired: 驱动配对流程 |
| OTA 指令超时 | JLOtaTimeoutManager 触发 handleTimeout: | 按指令重发或终止升级,cmdTimeOut: 可调 |
| OTA 传输丢包过多 | maxLostCount: 超限 | 判定链路异常,终止并提示重试;升级中断线由 JLOtaReConnectMgr 自动重连 |
| 升级后设备重启断连 | didDisconnectPeripheral:error: | updateRelinkType: 按 MAC/UUID 重连(JL_OTAResultReconnectWithMacAddr / ...UUid) |
| 强制升级失败 | otaStatus == JL_OtaStatusForce | handleWeatherNeedUpdate 返回 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 未开始即失败,需提示用户检查网络。
扩展点
- 新增连接模式:
JLBleHandler的每个方法都是「模式路由」模板,新增第三种连接栈只需在ToolsHelper.isConnectBySDK之外增加分支,并在EventChannelHandler.handleAllNotifications增加对应通知命名空间; - 自定义 OTA 指令:
JL_OTALib.framework提供JLOtaCustom(initWithDelegate:OtaManager:、cmdSendCommandData:needResponse:Result:),可在标准 OTA 流程之外透传自定义 RCSP 指令(如产测指令); - 重连策略定制:
JLOtaReConnectOption(serviceUUID/writeUUID/readUUID/authKey/deviceAuthorize/isWriteWithResponse)允许按项目定制 OTA 重连的 GATT 服务与鉴权参数,支持defaultOption一键恢复; - 设备类型扩展:
kDeviceType*常量集合可扩展新产品形态,配合JLOtaDeviceInfoParser parseInfo:intoManager:解析出的分区/状态信息驱动差异化升级 UI; - 事件协议扩展:
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 流程