广播音箱升级
本文档介绍 iOS-JL_OTA 中「广播音箱升级」能力的完整实现:通过 BLE 扫描、连接、配对(RCSP 协议)并串行调度多台音箱执行固件 OTA 升级的端到端工作流,涵盖 BroadcastBleManager、BroadcastThread、DeviceManager 三个核心模块及其协作机制。
Purpose and Scope
本页面聚焦于 code/JL_OTA/JL_OTA/BroadcastSpeakers/BroadcastBle/ 目录下的广播音箱批量升级子系统,说明其如何:
- 基于 CoreBluetooth 扫描/连接多台音箱,并经由杰理 SDK(
JL_Assist)建立 RCSP 命令通道; - 以单线程队列的方式对多台设备串行执行 OTA 固件升级(同一时刻仅一台设备在升级);
- 通过设备注册表(
DeviceManager)维护外围设备与 SDK 管理器的映射; - 通过通知(
kBDM_BLE_*)与代理协议(OtaUpdatePtl)向 UI 层上报状态与进度。
与之相关的单设备 OTA 流程、SDK 命令解析(JL_RunSDK、SDKBleHelper)以及普通 BLE 管理(JLBleManager)属于其他目录页面的范畴,本页仅在必要时引用,不展开叙述。
Overview
广播音箱升级(Broadcast Speaker Upgrade)是 JL_OTA 演示工程中面向「一拖多」场景的升级方案。与单设备 OTA 不同,广播场景的核心诉求是:在保持蓝牙广播连接的前提下,对多台同型号音箱批量烧录同一份固件。
系统由三个单例(singleton)协作完成:
| 模块 | 文件 | 职责 |
|---|---|---|
BroadcastBleManager | BroadcastBleManager.m | 蓝牙扫描、连接、断开、JL_Assist 会话管理、状态通知广播 |
BroadcastThread | BroadcastThread.m | OTA 任务队列与串行调度(maxVerify 并发闸门) |
DeviceManager | DeviceManager.m | 设备注册表:维护 CBPeripheral ↔ JL_EntityM/JLDeviceInfo 映射 |
关键设计决策:
- 并发闸门(maxVerify):
BroadcastThread通过maxVerify计数器保证任意时刻只有一台设备在执行升级,避免多路 RCSP 命令交错导致音箱侧协议状态机错乱。 - 按 UUID 隔离 Assist 会话:
BroadcastBleManager以cbp.identifier.UUIDString为 key 保存独立的JL_Assist实例,使多设备的命令通道互不干扰。 - 独立串行队列:OTA 数据发送运行在
dispatch_queue_create("ota_thread", NULL)队列上,与主线程解耦,保证大数据包发送不阻塞 UI。
Architecture
flowchart TD
subgraph sg_UI["UI 层 (ViewController)"]
VC["FittingView / 升级界面"]
end
subgraph sg_Broadcast["广播升级子系统 BroadcastSpeakers"]
BBM["BroadcastBleManager<br/>(单例: 扫描/连接/Assist)"]
BT["BroadcastThread<br/>(单例: OTA 调度队列)"]
DM["DeviceManager<br/>(单例: 设备注册表)"]
BBM -->|"kBDM_BLE_* 通知"| VC
BT -->|"OtaUpdatePtl 代理回调"| VC
VC -->|"startOta: 提交任务"| BT
BT -->|"checkoutWith: 查询设备"| DM
BT -->|"读取 assistDicts 会话"| BBM
DM -->|"addDevicesWithSDKEntity:"| BBM
end
subgraph sg_BLE["BLE 物理层"]
CB["CBCentralManager / CBPeripheral"]
end
subgraph sg_SDK["杰理 SDK"]
JA["JL_Assist (每设备独立实例)"]
JLE["JLBleEntity"]
end
BBM -->|"CoreBluetooth 代理"| CB
BBM -->|"makeAssist: 创建会话"| JA
BBM --> JA
DM --> JLE
CB -.->|"广播发现"| BBM
架构说明
- 入口:UI 层(如
FittingView相关控制器)向BroadcastThread提交一组BroadcastOtaInfo(包含cbp外围设备与updatePath固件路径)。 - 调度:
BroadcastThread首件立即执行、其余入队,next在每件完成后推进队列。 - 通道:升级命令经由
BroadcastBleManager中按 UUID 缓存的JL_Assist实例下发到AE00服务 /AE01写通道 /AE02读通道。 - 反馈:进度与结果通过
OtaUpdatePtl代理(otaResultIsBegin:、otaResult:Status:Progress:)回传 UI。
核心模块实现
BroadcastBleManager —— BLE 会话与通知中枢
BroadcastBleManager 同时实现 CBCentralManagerDelegate 与 CBPeripheralDelegate,是广播升级的蓝牙底座。其对外状态全部以 NSNotification 形式发布,UI 层据此驱动界面:
NSString *kBDM_BLE_FOUND = @"BDM_BLE_FOUND"; //发现设备
NSString *kBDM_BLE_PAIRED = @"BDM_BLE_PAIRED"; //BLE已配对
NSString *kBDM_BLE_CONNECTED = @"BDM_BLE_CONNECTED"; //BLE已连接
NSString *kBDM_BLE_DISCONNECTED = @"BDM_BLE_DISCONNECTED"; //BLE断开连接
NSString *BLE_SERVICE = @"AE00"; //服务号
NSString *BLE_RCSP_W = @"AE01"; //命令"写"通道
NSString *BLE_RCSP_R = @"AE02"; //命令"读"通道
Source: BroadcastBleManager.m
设计意图:AE00/AE01/AE02 是杰理 RCSP 协议的标准服务/特征 UUID。BroadcastBleManager 不直接依赖具体外设型号,而是把「服务号 + 读写特征」配置为常量,任何遵循该协议的音箱都能接入,这保证了广播批量升级的通用性。
会话创建(makeAssist:) 是连接成功后的关键步骤——为每个外设按 UUID 分配独立的 JL_Assist:
-(void)makeAssist:(CBPeripheral *)cbp{
JL_Assist *assist = _assistDicts[cbp.identifier.UUIDString];
if (assist){
_mAssist = assist;
}else{
/*--- JLSDK ADD ---*/
assist = [[JL_Assist alloc] init];
[_assistDicts setValue:assist forKey:cbp.identifier.UUIDString];
_mAssist = assist;
_mAssist.mNeedPaired = [ToolsHelper isSupportPair]; //是否需要握手配对
_mAssist.mPairKey = nil; //配对秘钥(或者自定义配对码pairData)
_mAssist.mService = BLE_SERVICE; //服务号
_mAssist.mRcsp_W = BLE_RCSP_W; //特征「写」
_mAssist.mRcsp_R = BLE_RCSP_R; //特征「读」
}
}
Source: BroadcastBleManager.m
设计意图:mNeedPaired 由 [ToolsHelper isSupportPair] 决定,意味着「是否需要握手配对」是可配置的产品行为;mPairKey = nil 表示使用 SDK 默认配对逻辑,若需要自定义 16 字节配对码,可参考注释中 pairkey 数组改为传入 pairData。_assistDicts 缓存保证同一外设重连后复用既有会话,避免重复初始化。
扫描控制:startScanBLE 在 CBManagerStatePoweredOn 时才真正发起 scanForPeripheralsWithServices:nil,否则延迟 0.5 秒重试,处理了蓝牙未就绪的时序问题;stopScanBLE 立即停止扫描。
BroadcastThread —— OTA 串行调度器
BroadcastThread 是升级编排的核心,负责把一批设备排队、逐台执行,并把进度回调给所有注册的代理(delegates)。
队列初始化与并发闸门:
- (instancetype)init
{
self = [super init];
if (self) {
_reconnectDict = [NSMutableDictionary new];
self.senderThread = dispatch_queue_create("ota_thread", NULL);
self.verifyList = [NSMutableArray new];
}
return self;
}
-(void)startOta:(NSArray <BroadcastOtaInfo*>*)items{
self.verifyList = [NSMutableArray new];
self.maxVerify = 1;
for (BroadcastOtaInfo *objc in items) {
if(self.maxVerify>1){
[self.verifyList addObject:objc];
}else{
[self threadRun:objc];
}
}
}
-(void)next{
self.maxVerify--;
if(self.verifyList.count>0){
BroadcastOtaInfo *objc = self.verifyList[0];
[self threadRun:objc];
[self.verifyList removeObject:objc];
}
}
Source: BroadcastThread.m
设计意图:maxVerify 是典型的令牌计数闸门——startOta: 时置为 1,第一台设备直接进入 threadRun:(其中 maxVerify++ 变为 2),其余设备全部入队;每完成一台调用 next 递减令牌并取出队首执行。这样既实现了「首件立即执行」的体验优化,又严格保证 threadRun: 的并发度不超过 1。
单机升级执行(threadRun:):
-(void)threadRun:(BroadcastOtaInfo *)info{
self.maxVerify++;
JLDeviceInfo *device = [[DeviceManager share] checkoutWith:info.cbp];
JL_Assist *assist = [[BroadcastBleManager sharedInstance] assistDicts][info.cbp.identifier.UUIDString];
for (id<OtaUpdatePtl>item in self.delegates) {
dispatch_async(dispatch_get_main_queue(), ^{
if([item respondsToSelector:@selector(otaResultIsBegin:)]){
[item otaResultIsBegin:info.cbp];
}
});
}
if(info){
NSData *data = [NSData dataWithContentsOfFile:info.updatePath];
if(assist == nil){
for (id<OtaUpdatePtl>objc in self.delegates) {
dispatch_async(dispatch_get_main_queue(), ^{
if([objc respondsToSelector:@selector(otaResult:Status:Progress:)]){
[objc otaResult:info.cbp Status:JL_OTAResultFailTWSDisconnect Progress:0.0];
}
});
}
}
// ... 后续通过 assist 下发固件数据并上报进度
}
}
Source: BroadcastThread.m
设计意图:threadRun: 首先从 DeviceManager 取出设备信息、从 BroadcastBleManager 取出对应 Assist 会话,然后立刻通过 otaResultIsBegin: 通知 UI「该设备升级开始」。若会话缺失(例如设备已断开导致 assistDicts 中无对应项),则直接以 JL_OTAResultFailTWSDisconnect 失败状态回调——这是一个防御性检查,避免对无通道的设备盲目发数据。所有 UI 回调都被 dispatch_async 到主队列,保证线程安全。
DeviceManager —— 设备注册表
DeviceManager 维护 JLDeviceInfo 数组,提供设备增删查操作:
-(void)addDevicesWithSDKEntity:(JL_EntityM *)entity{
JLBleEntity *entityBasic = [[JLBleEntity alloc] init];
entityBasic.mRSSI = entity.mRSSI;
entityBasic.mPeripheral = entity.mPeripheral;
entityBasic.bleMacAddress = entity.mBleAddr;
entityBasic.mType = entity.mType;
[self addDevicesEntity:entityBasic WithManager:entity.mCmdManager];
}
-(JLDeviceInfo * _Nullable)checkoutWith:(CBPeripheral *)peripheral{
for (JLDeviceInfo *info in self.devices) {
JLBleEntity *item = info.entity;
if ([item.mPeripheral.identifier.UUIDString isEqualToString:peripheral.identifier.UUIDString]){
return info;
}
}
return nil;
}
Source: DeviceManager.m 与 DeviceManager.m
设计意图:addDevicesWithSDKEntity: 是 SDK 发现回调与本地注册表之间的适配器,将 SDK 的 JL_EntityM(含 mCmdManager 命令管理器)扁平化为 JLBleEntity + JL_ManagerM 组合;checkoutWith: 以 CBPeripheral.identifier.UUIDString 作为稳定主键(区别于 MAC 地址可能变化的情况)。JLDeviceInfo 还暴露了 cmdGetSystemInfo 等命令方法,供后续扩展读取设备系统信息。
核心工作流
端到端升级时序
sequenceDiagram
participant UI as UI 层 (FittingView)
participant BBM as BroadcastBleManager
participant DM as DeviceManager
participant BT as BroadcastThread
participant SDK as JL_Assist / SDK
participant DEV as 音箱 (CBPeripheral)
UI->>BBM: startScanBLE
BBM->>DEV: scanForPeripheralsWithServices
DEV-->>BBM: 广播发现 (didDiscover)
BBM-->>UI: 通知 kBDM_BLE_FOUND
UI->>BBM: 发起连接
BBM->>DEV: connectPeripheral
DEV-->>BBM: didConnect
BBM->>BBM: makeAssist: 按 UUID 创建 JL_Assist
BBM-->>UI: 通知 kBDM_BLE_CONNECTED / kBDM_BLE_PAIRED
BBM->>DM: addDevicesWithSDKEntity (注册设备)
UI->>BT: startOta: [BroadcastOtaInfo...]
BT->>BT: 首件 threadRun, 其余入 verifyList (maxVerify=1)
BT->>DM: checkoutWith: cbp 查设备
BT->>BBM: 读取 assistDicts[UUID] 会话
BT-->>UI: otaResultIsBegin: 升级开始
BT->>SDK: 经 mRcsp_W 下发固件数据 (updatePath)
SDK->>DEV: RCSP 写通道 AE01
DEV-->>SDK: 进度/结果 (读通道 AE02)
SDK-->>BT: OTA 进度与状态回调
BT-->>UI: otaResult:Status:Progress
BT->>BT: next (maxVerify--, 弹出队首)
Note over BT: 循环执行下一台设备
关键流程说明
- 发现与连接:
BroadcastBleManager扫描到设备后以kBDM_BLE_FOUND通知 UI;UI 选择目标设备发起连接。 - 会话建立:连接成功后在
makeAssist:中按 UUID 创建/复用JL_Assist,配置AE00/AE01/AE02服务特征,并按ToolsHelper开关决定是否握手配对。 - 设备注册:SDK 发现回调通过
DeviceManager addDevicesWithSDKEntity:把JL_EntityM转为JLDeviceInfo入库。 - 批量入队:UI 组装
BroadcastOtaInfo数组(每项含cbp与updatePath)调用startOta:;首件立即执行,其余进入verifyList。 - 串行升级:
threadRun:逐台读取固件、经 Assist 下发;每台完成后next推进队列,直至全部完成。 - 结果反馈:任何一步失败(如 Assist 会话缺失)都会以
JL_OTAResultFailTWSDisconnect回调,UI 可据此展示失败原因并继续下一台。
使用示例
提交一批设备的 OTA 升级
UI 层批量发起升级的入口调用(摘自 BroadcastThread 对外接口的实际使用方式):
// 组装升级任务数组:每项包含目标外设与固件路径
NSArray<BroadcastOtaInfo *> *items = [self buildOtaItems]; // items[i].cbp / items[i].updatePath
[[BroadcastThread share] startOta:items];
说明:
BroadcastOtaInfo携带cbp(CBPeripheral)与updatePath(固件文件路径),threadRun:内部通过[NSData dataWithContentsOfFile:info.updatePath]读取固件。见 BroadcastThread.m。
注册 UI 代理接收进度
BroadcastThread 通过 delegates(实现 OtaUpdatePtl 协议)广播升级事件,UI 在收到 otaResultIsBegin: 时切换为升级中界面,在 otaResult:Status:Progress: 中更新进度条:
for (id<OtaUpdatePtl>item in self.delegates) {
dispatch_async(dispatch_get_main_queue(), ^{
if([item respondsToSelector:@selector(otaResultIsBegin:)]){
[item otaResultIsBegin:info.cbp]; // 通知 UI:该设备升级开始
}
});
}
Source: BroadcastThread.m
监听蓝牙状态通知
UI 监听 BroadcastBleManager 发布的通知,驱动扫描/连接界面:
// 通知常量由 BroadcastBleManager.m 定义
NSString *kBDM_BLE_FOUND = @"BDM_BLE_FOUND"; //发现设备
NSString *kBDM_BLE_PAIRED = @"BDM_BLE_PAIRED"; //BLE已配对
NSString *kBDM_BLE_CONNECTED = @"BDM_BLE_CONNECTED"; //BLE已连接
NSString *kBDM_BLE_DISCONNECTED = @"BDM_BLE_DISCONNECTED"; //BLE断开连接
Source: BroadcastBleManager.m
设备注册(SDK 发现回调适配)
-(void)addDevicesWithSDKEntity:(JL_EntityM *)entity{
JLBleEntity *entityBasic = [[JLBleEntity alloc] init];
entityBasic.mRSSI = entity.mRSSI;
entityBasic.mPeripheral = entity.mPeripheral;
entityBasic.bleMacAddress = entity.mBleAddr;
entityBasic.mType = entity.mType;
[self addDevicesEntity:entityBasic WithManager:entity.mCmdManager];
}
Source: DeviceManager.m
配置选项
广播升级子系统的可调配置集中在 BroadcastBleManager 的常量与 JL_Assist 属性上:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
BLE_SERVICE | NSString | @"AE00" | RCSP 服务号(UUID 前缀) |
BLE_RCSP_W | NSString | @"AE01" | 命令「写」特征 |
BLE_RCSP_R | NSString | @"AE02" | 命令「读」特征 |
assist.mNeedPaired | BOOL | [ToolsHelper isSupportPair] | 是否需要握手配对,由工具类开关决定 |
assist.mPairKey | NSData | nil | 配对秘钥;nil 走 SDK 默认配对,可传 16 字节自定义配对码 |
maxVerify | NSInteger | 1(启动时) | 并发闸门,恒为 1 保证串行升级 |
senderThread | dispatch_queue_t | "ota_thread" | OTA 数据发送专用串行队列 |
API 参考
BroadcastBleManager
| 方法/属性 | 签名要点 | 说明 |
|---|---|---|
sharedInstance | + (instancetype)sharedInstance | 全局单例,dispatch_once 保证只初始化一次 |
init | - (instancetype)init | 初始化 CBCentralManager(delegate 为 self)、assistDicts、blePeripheralArr、connectDict |
makeAssist: | - (void)makeAssist:(CBPeripheral *)cbp | 按外设 UUID 创建或复用 JL_Assist 会话,配置服务/特征与配对参数 |
startScanBLE | - (void)startScanBLE | 蓝牙就绪后开始扫描(scanForPeripheralsWithServices:nil),未就绪则延迟 0.5s 重试 |
stopScanBLE | - (void)stopScanBLE | 停止扫描 |
assistDicts | NSMutableDictionary | 以 UUIDString 为 key 的 JL_Assist 会话表,供 BroadcastThread 读取 |
通知(NSNotificationCenter 广播):
| 通知名 | 含义 |
|---|---|
kBDM_BLE_FOUND | 发现新设备(BDM_BLE_FOUND) |
kBDM_BLE_PAIRED | BLE 已完成配对握手(BDM_BLE_PAIRED) |
kBDM_BLE_CONNECTED | BLE 已连接(BDM_BLE_CONNECTED) |
kBDM_BLE_DISCONNECTED | BLE 断开连接(BDM_BLE_DISCONNECTED) |
BroadcastThread
| 方法 | 签名要点 | 说明 |
|---|---|---|
share | + (instancetype)share | 全局单例 |
startOta: | - (void)startOta:(NSArray<BroadcastOtaInfo *> *)items | 批量提交升级任务:重置队列与 maxVerify=1,首件立即执行,其余入队 |
next | - (void)next | 递减 maxVerify,弹出队首任务继续执行;无任务时保持空闲 |
threadRun: | - (void)threadRun:(BroadcastOtaInfo *)info | 单台设备升级执行体:查设备、取 Assist、读固件、回调开始/进度/结果 |
delegates | NSArray<id<OtaUpdatePtl>> | 升级事件代理列表,UI 注册后接收回调 |
代理协议 OtaUpdatePtl 回调(均在主队列派发):
otaResultIsBegin:(CBPeripheral *)cbp:某设备升级开始。otaResult:(CBPeripheral *)cbp Status:(JL_OTAResult)status Progress:(float)progress:升级状态与进度,status取值含JL_OTAResultFailTWSDisconnect(会话缺失/断开导致的失败)等 SDK 枚举。
DeviceManager / JLDeviceInfo
| 方法 | 签名要点 | 说明 |
|---|---|---|
share | + (instancetype)share | 全局单例,持有 devices 数组 |
addDevicesWithSDKEntity: | - (void)addDevicesWithSDKEntity:(JL_EntityM *)entity | SDK 发现回调适配:JL_EntityM → JLBleEntity 并入库 |
addDevicesEntity:WithManager: | - (void)addDevicesEntity:(JLBleEntity *)entity WithManager:(JL_ManagerM *)manager | 按 UUID 去重后新增 JLDeviceInfo |
removeDevicesBy: | - (void)removeDevicesBy:(CBPeripheral *)cbp | 按外设移除注册项 |
checkoutWith: | - (JLDeviceInfo * _Nullable)checkoutWith:(CBPeripheral *)peripheral | 按 UUID 查询设备,未找到返回 nil |
JLDeviceInfo.test | - (void)test | 示例命令:cmdGetSystemInfo:JL_FunctionCodeCOMMON SelectionBit:0x4000 读取系统信息 |
失败模式、边界情况与并发
失败模式
- Assist 会话缺失(升级前检查):
threadRun:中assist为nil时直接回调JL_OTAResultFailTWSDisconnect,不发送任何数据。触发条件通常是设备在入队后、执行前断开,或makeAssist:尚未完成。 - 固件文件读取失败:
[NSData dataWithContentsOfFile:info.updatePath]返回空数据时后续升级自然失败;调用方需保证updatePath指向有效文件。 - 蓝牙未就绪扫描:
startScanBLE在CBManagerStatePoweredOn之前调用会延迟 0.5s 重试一次,但仍可能在系统蓝牙关闭时扫描无果——UI 应结合系统蓝牙状态提示用户。
边界情况
- 重复注册:
addDevicesEntity:WithManager:以identifier.UUIDString去重,同一外设不会重复入库;但若同一设备重新广播,SDK 可能生成新CBPeripheral对象,UUID 保持不变时仍可命中缓存。 - 断开后的队列:
next不感知设备连接状态,队列会继续推进到下一台;失败设备的状态通过代理回调告知 UI,由 UI 决定是否重试。 - 空任务数组:
startOta:传入空数组时仅重置队列与闸门,不执行任何升级,行为安全。
并发与线程安全
- 串行执行保证:
maxVerify令牌闸门确保threadRun:并发度 ≤ 1,避免多台音箱的 RCSP 命令在蓝牙链路上交错。 - 独立会话隔离:每台设备持有独立的
JL_Assist(assistDicts[UUID]),命令序号(SN)互不干扰。 - UI 回调线程:所有
OtaUpdatePtl回调均dispatch_async到主队列,UI 无需额外加锁。 - 发送队列:固件数据发送运行在专用串行队列
"ota_thread"上,与主线程解耦;但该队列与 CoreBluetooth 代理回调(queue:nil即主队列)之间的同步依赖 SDK 内部机制,扩展时不宜在代理回调中直接阻塞等待升级完成。
性能与运维注意事项
- 数据发送吞吐:固件下发走
AE01写通道,实际速率受 BLE MTU 与JL_Assist分片策略约束;串行设计使单设备吞吐最大化,但总时长随设备数量线性增长,批量场景需在 UI 上展示「第 N 台 / 共 M 台」。 - 内存占用:
threadRun:一次性dataWithContentsOfFile:读入整个固件,大固件(数 MB)会瞬时占用较多内存;广播场景若同时缓存多份固件需注意内存水位。 - 扫描功耗:
startScanBLE使用scanForPeripheralsWithServices:nil全量扫描,持续扫描耗电较高,建议在连接目标设备后立即stopScanBLE。 - 重连缓存:
BroadcastBleManager维护lastUUID、lastBleMacAddressList、connectDict等重连辅助数据,说明系统支持断线重连的运维预期;具体重连策略由CBCentralManagerDelegate回调驱动。
扩展点
OtaUpdatePtl代理协议:UI 层实现该协议即可接入升级进度,无需改动调度逻辑;多代理注册可同时驱动多个界面(如列表页 + 详情页)。- 自定义配对码:在
makeAssist:中将mPairKey设为自定义 16 字节pairData(代码中有注释示例),可适配需要固定配对码的产线/渠道设备。 ToolsHelper isSupportPair:作为「是否需要握手配对」的全局开关,修改该工具方法即可在不改升级流程的情况下切换新旧协议设备。DeviceManager设备注册:JLDeviceInfo的test方法展示了基于cmdGetSystemInfo扩展设备信息查询的范式,可在升级前做型号/固件版本校验。maxVerify闸门语义:当前恒为 1 实现严格串行;若未来协议支持并发通道,可将maxVerify提升为可配置值以支持「N 台并行」。
测试与验证建议
仓库内该目录未提供独立单元测试文件,验证主要依赖真机联调:
- 单台冒烟:连接一台音箱,提交单元素数组,验证
otaResultIsBegin:→ 进度递增 → 成功回调的完整链路。 - 批量串行验证:连接 2~3 台设备,观察
threadRun:是否严格逐台执行(可通过maxVerify日志或设备端日志确认无并发)。 - 断线注入:升级中途断开设备,验证
assist缺失分支回调JL_OTAResultFailTWSDisconnect且队列继续推进。 - 配对开关:切换
ToolsHelper isSupportPair,验证kBDM_BLE_PAIRED通知在两种模式下均能正确触发。
Related Links
- 广播音箱 BLE 管理器源码(扫描/连接/会话:
BroadcastBleManager.m) - 广播音箱 OTA 调度器源码(队列与串行升级:
BroadcastThread.m) - 设备注册表源码(
DeviceManager.m/JLDeviceInfo) - 相关能力页:单设备 OTA 流程(
SDKBleManager/JL_RunSDK.m、SDKBleHelper.m)、通用 BLE 管理(BleManager/JLBleManager.m)、SDK 命令协助(BleManager/JLBleEntity.m)