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

    • 项目概述与功能特性
    • 工程结构与运行环境
  • 快速开始

    • SDK 集成步骤
    • 连接方式选择指南
  • 核心 SDK 架构

    • SDK 框架组成
    • JL_OTAManager 升级管理 API
    • 设备认证与广播解析
  • 蓝牙连接与设备发现

    • 设备扫描与广播发现
    • 原生 CoreBluetooth 连接
    • JL_BLEKit SDK 连接
    • JL_Assist 自定义连接
    • GATT Over BR/EDR 经典蓝牙升级
  • OTA 升级工作流

    • 标准升级流程
    • 自动化测试与批量升级
    • 广播音箱升级
    • 升级文件管理
  • 示例工程

    • 完整示例应用
    • 迷你示例工程
    • 第三方依赖与工具
  • 开发支持与版本发布

    • 文档中心与 API 说明
    • SDK 版本与构建产物
    • 调试技巧与日志辅助

自动化测试与批量升级

JL_OTA Demo 中基于 LoopUpdateManager 构建的自动化测试与批量升级能力:通过配置循环次数,将同一设备连续执行多轮固件升级(OTA),并在每次升级完成、设备重启后自动重连并进入下一轮,实现无人值守的升级稳定性压力测试与多文件批量升级。

Purpose and Scope

本页面介绍 iOS-JL_OTA 工程中"自动化测试与批量升级"(Auto Test OTA / Loop Update)完整机制,涵盖:

  • LoopUpdateManager 升级队列的构建、推进与容错逻辑;
  • ToolsHelper 中自动化测试配置项的读写与持久化;
  • 升级完成后的自动重连闭环(BLE/HID);
  • 相关 UI 视图(AutoDevicesViewController、AutoFinishView、TipsFinishView、TipsProgressView)如何驱动队列启停。

以下内容属于其他页面范畴,不在本文展开:BLE 底层扫描/连接实现(见 JLBleManager 相关页面)、OTA 固件传输协议细节(见 OTA 升级流程页面)、UI 布局样式。

Overview

在耳机/音箱等 BLE 设备的量产或研发验证阶段,通常需要对固件升级链路做高强度的稳定性验证——同一台设备反复升级数十次,观察是否出现升级失败、连接丢失、设备变砖等问题。手工逐次操作既低效又易出错。

为此,JL_OTA Demo 内置了**自动化测试升级(Auto Test OTA)**功能:

  1. 用户通过 ToolsHelper 配置循环升级次数 N 与容错参数;
  2. LoopUpdateManager 将选择的固件文件按 N 次展开为升级队列;
  3. 逐次调用 JLBleManager 执行 OTA,每轮升级完成后记录设备 UUID,等待设备重启;
  4. 设备重启后自动重连(支持 HID 与普通 BLE 两种方式),并自动开始下一轮;
  5. 队列耗尽或出错(达到容错上限)时停止,整个过程无需人工干预。

其本质是一个串行队列 + 自动重连循环的状态机:升级中 → 完成/失败 → 重连 → 下一轮。队列长度、容错开关、容错次数均由本地 NSUserDefaults(通过 DFTools 封装)持久化。

Architecture

flowchart TD
    subgraph sg_UI["UI 层(视图/控制器)"]
        AutoDevicesVC["AutoDevicesViewController"]
        AutoFinishView["AutoFinishView"]
        TipsFinishView["TipsFinishView"]
        TipsProgressView["TipsProgressView"]
    end

    subgraph sg_Core["核心层"]
        LoopMgr["LoopUpdateManager(单例)"]
        LoopInfo["LoopInfo(进度模型)"]
    end

    subgraph sg_Config["配置层"]
        ToolsHelper["ToolsHelper"]
    end

    subgraph sg_Ble["蓝牙层"]
        JLBleManager["JLBleManager"]
        Peripheral["BLE/HID 设备"]
    end

    AutoDevicesVC -->|"startLoopUpdate / toNextUpdate"| LoopMgr
    AutoFinishView -->|"shouldLoopUpdate / cleanList"| LoopMgr
    TipsFinishView -->|"cleanList(出错终止)"| LoopMgr
    TipsProgressView -->|"shouldLoopUpdate / reConnectByAutoUpdate"| LoopMgr
    LoopMgr --> LoopInfo
    LoopMgr -->|"isAutoTestOta / getAutoTestOtaNumber / getFaultTolerant"| ToolsHelper
    LoopMgr -->|"otaFuncWithFilePath / connectPeripheralWithUUID / findHid"| JLBleManager
    JLBleManager --> Peripheral

架构说明:LoopUpdateManager 是整个机制的中枢,UI 层只负责触发与展示,不直接操作蓝牙;配置层 ToolsHelper 向队列管理器提供开关与次数;蓝牙层 JLBleManager 是唯一的传输执行者,负责固件下发(otaFuncWithFilePath:)与重连(connectPeripheralWithUUID:/findHid:)。LoopInfo 作为轻量进度模型,承载当前文件名与"第几个/共几个"的展示信息。

核心实现:LoopUpdateManager 队列管理

LoopUpdateManager 位于 Tools/LoopUpdateManager.m,采用 dispatch_once 单例模式,内部持有一个可变数组 updatePath 作为升级队列,并保留重连 UUID、完成次数、失败次数、状态等运行期信息。

单例与初始化

+(instancetype)share{
    static LoopUpdateManager *loopMgr;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        loopMgr = [LoopUpdateManager new];
    });
    return loopMgr;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        updatePath = [NSMutableArray new];
        self.info = [LoopInfo new];
    }
    return self;
}

Source: LoopUpdateManager.m

设计意图:整个 App 生命周期内只需一个队列实例,所有视图(自动测试页、完成弹窗、进度弹窗)通过 [LoopUpdateManager share] 访问同一份队列状态,避免多实例导致队列不同步。初始化时创建空的 updatePath 数组并预置 LoopInfo 进度对象。

队列构建:startLoopUpdate:

-(BOOL)startLoopUpdate:(NSArray *)filePaths{
    if(updatePath.count > 0){
        [self toNextUpdate];
        return YES;
    }
    
    self.reConnectUUID = [JLBleManager sharedInstance].mBlePeripheral.identifier.UUIDString;
    kJLLog(JLLOG_DEBUG, @"self.reConnectUUID:%@",self.reConnectUUID);
    _finishNumber = 0;
    NSInteger number = [ToolsHelper getAutoTestOtaNumber];
    if(number == 0){
        return NO;
    }
    int count = 0;
    while(count < number){
        for (NSString *path in filePaths) {
            NSString *filePath = [DFFile listPath:NSDocumentDirectory MiddlePath:@"upgrade" File:path];
            [updatePath addObject:filePath];
            count++;
            if (count == number){
                break;
            }
        }
    }
    kJLLog(JLLOG_DEBUG, @"auto update count:%d",updatePath.count);
    
    [[JLBleManager sharedInstance] otaFuncWithFilePath:updatePath.firstObject];
    
    self.info.name = [updatePath.firstObject lastPathComponent];
    
    self.info.nowIndexStr = [NSString stringWithFormat:@"%d/%d",(int)self.finishNumber+1,(int)number];
    return YES;
}

Source: LoopUpdateManager.m

关键逻辑:

  • 幂等保护:若队列非空说明上一轮尚未结束,直接调用 toNextUpdate 推进而不是重建队列;
  • 记录重连目标:把当前外设的 identifier.UUIDString 保存为 reConnectUUID,供设备重启后重连使用;
  • 队列展开:从 ToolsHelper 读取循环次数 number,将传入的文件列表按需重复填充进 updatePath,直到总数达到 N。这意味着同一份固件可以循环升级 N 次,也支持多个文件按序批量升级;
  • 立即开跑:取队首文件调用 otaFuncWithFilePath: 开始第一轮升级,并通过 LoopInfo 记录当前文件名与 1/N 进度。

队列推进:toNextUpdate

-(BOOL)toNextUpdate{
    
    if([ToolsHelper getFaultTolerant]){
        if([ToolsHelper getFaultTolerantTimes] > self.failedNumber){
            if(_status == DeviceOtaStatusPrepare || _status == DeviceOtaStatusFinish){
                @try {
                    [updatePath removeObjectAtIndex:0];
                } @catch (NSException *exception) {
                    kJLLog(JLLOG_DEBUG, @"自动升级队列为空");
                    return NO;
                } @finally {
                    
                }
            }
        }else{
            
        }
    }else{
        @try {
            [updatePath removeObjectAtIndex:0];
        } @catch (NSException *exception) {
            kJLLog(JLLOG_DEBUG, @"自动升级队列为空");
            return NO;
        } @finally {
            
        }
    }
   
    if([ToolsHelper isAutoTestOta] && updatePath.count > 0){
        kJLLog(JLLOG_DEBUG, @"toNextUpdate 剩余:%d 次", updatePath.count);
        [[JLBleManager sharedInstance] otaFuncWithFilePath:updatePath.firstObject];
        self.info.name = [updatePath.firstObject lastPathComponent];
        NSInteger max = [ToolsHelper getAutoTestOtaNumber];
        self.info.nowIndexStr = [NSString stringWithFormat:@"%d/%d",(int)_finishNumber+1,(int)max];
        return true;
    }
    return NO;
}

Source: LoopUpdateManager.m

设计意图与容错策略:

  • 容错开关(fault_tolerant):开启时,仅当累计失败次数 failedNumber 小于容错阈值 fault_tolerant_times,且当前状态为 DeviceOtaStatusPrepare 或 DeviceOtaStatusFinish 时才弹出队首并继续;否则停在原地不再推进,等待外部处理。关闭容错时则无条件移除队首继续,即"失败也继续下一轮";
  • 空队列防护:用 @try/@catch 包裹 removeObjectAtIndex:0,队列为空时捕获异常并记录"自动升级队列为空",返回 NO 终止;
  • 推进条件:只有自动化测试开关 isAutoTestOta 为真且队列仍有剩余时才调用 otaFuncWithFilePath: 开启下一轮,并同步更新进度文案。

自动重连:startLoopOta

-(void)startLoopOta{
    if([ToolsHelper isAutoTestOta]){
        
        if([ToolsHelper isSupportHID]){
            [[JLBleManager sharedInstance] findHid:self.reConnectUUID];
        }else{
            [[JLBleManager sharedInstance] connectPeripheralWithUUID:self.reConnectUUID];
        }
    }
}

Source: LoopUpdateManager.m

设计意图:OTA 升级完成后设备会重启并断开蓝牙,必须等设备重新广播后再连接。该方法根据设备是否支持 HID 选择 findHid:(HID 重连)或 connectPeripheralWithUUID:(普通 BLE 按 UUID 重连),是"设备重启 → 自动回连 → 下一轮"闭环的入口,由进度弹窗在升级结束后调用。

队列状态查询与清理

-(BOOL)shouldLoopUpdate{
    if(updatePath.count>1) return YES;
    return NO;
}

-(void)cleanList{
    [updatePath removeAllObjects];
}

Source: LoopUpdateManager.m

shouldLoopUpdate 用于判断"当前是否处于批量升级模式"(队列中还有 1 个以上待升级项),UI 据此决定展示"继续升级"还是普通完成提示;cleanList 用于用户主动取消或错误发生时清空队列,终止整个自动化流程。

配置层:ToolsHelper 的自动化测试设置

自动化测试相关的所有配置均通过 ToolsHelper(Tools/ToolsHelper.m)读写,底层由 DFTools 封装 NSUserDefaults 持久化,App 重启后配置依然保留。

+(BOOL)isAutoTestOta{
    if([DFTools getUserByKey:@"AutoTestOta"]){
        return [[DFTools getUserByKey:@"AutoTestOta"] boolValue];
    }else{
        return NO;
    }
}

+(void)setAutoTestOta:(BOOL)status{
    [DFTools setUser:[NSNumber numberWithBool:status] forKey:@"AutoTestOta"];
}

+(NSInteger)getAutoTestOtaNumber{
    if([DFTools getUserByKey:@"AutoTestOtaNumber"]){
        kJLLog(JLLOG_DEBUG,@"getAutoTestOtaNumber:%d",[[DFTools getUserByKey:@"AutoTestOtaNumber"] intValue]);
        return [[DFTools getUserByKey:@"AutoTestOtaNumber"] intValue];
    }else{
        kJLLog(JLLOG_DEBUG, @"getAutoTestOtaNumber:1");
        return 1;
    }
}

+(void)setAutoTestOtaNumber:(NSInteger)number{
    kJLLog(JLLOG_DEBUG, @"setAutoTestOtaNumber:%d",number);
    [DFTools setUser:[NSNumber numberWithInt:(int)number] forKey:@"AutoTestOtaNumber"];
}

Source: ToolsHelper.m

容错配置的读写与之对称:setFaultTolerant:/getFaultTolerant 使用键 fault_tolerant(BOOL),setFaultTolerantTimes:/getFaultTolerantTimes 使用键 fault_tolerant_times(NSInteger),读取时均对"未设置"情形给出安全默认值。

Source: ToolsHelper.m

设计意图:把配置读写收敛到 ToolsHelper 单一入口,队列管理器、UI 各视图只需调用语义化方法,无需关心存储键与默认值细节;所有值用 NSNumber 包装存储,保证类型安全。

核心流程:一轮完整的多轮升级闭环

sequenceDiagram
    participant VC as AutoDevicesViewController
    participant LM as LoopUpdateManager
    participant TH as ToolsHelper
    participant BM as JLBleManager
    participant DV as 目标设备

    VC->>LM: startLoopUpdate:filePaths
    LM->>TH: getAutoTestOtaNumber
    TH-->>LM: 循环次数 N(默认 1)
    LM->>LM: 展开队列 updatePath(共 N 个文件路径)
    LM->>BM: otaFuncWithFilePath: 队首固件
    BM->>DV: 传输并升级固件
    Note over BM,DV: 升级完成,设备重启断开
    BM-->>LM: 状态变为 DeviceOtaStatusFinish
    LM->>LM: toNextUpdate(按容错规则移除队首)
    LM->>BM: startLoopOta → findHid/connectPeripheralWithUUID
    BM->>DV: 自动重连成功
    LM->>BM: otaFuncWithFilePath: 下一个固件
    Note over LM: 循环执行直至队列耗尽<br/>或失败次数达到容错上限

流程要点:

  1. 启动:自动测试设备页调用 startLoopUpdate:,队列按配置次数 N 展开,立即发起首轮 OTA;
  2. 升级:JLBleManager 负责固件传输;期间进度弹窗 TipsProgressView 在升级推进时检查 shouldLoopUpdate,若处于批量模式则展示自动重连提示(reConnectByAutoUpdate);
  3. 完成/失败:升级结束后调用 toNextUpdate 推进队列——容错开启时仅在状态为 Prepare/Finish 且失败次数未超限时继续;出错时 TipsFinishView 调用 cleanList 终止流程并提示"发生错误导致关闭自动化升级";
  4. 重连:startLoopOta 按 isSupportHID 选择 HID 或 BLE 重连到 reConnectUUID 对应的设备;
  5. 循环:重连成功后自动开始下一轮,直到队列清空或用户确认终止。

UI 集成:视图如何驱动队列

自动测试设备页

AutoDevicesViewController(Views/AutoTestUpdate/AutoDevicesView/)是自动化测试的入口界面。在自动升级刚开始触发时,它直接驱动队列前进:

//当自动刚升级时触发以下方法
[[LoopUpdateManager share] toNextUpdate];

Source: AutoDevicesViewController.m

完成弹窗 AutoFinishView

升级全部完成后,若仍处于批量模式(shouldLoopUpdate 为 YES),弹窗提示"当前存在升级任务",用户确认后调用 cleanList 清空队列结束流程:

if ([[LoopUpdateManager share] shouldLoopUpdate]){
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:kJL_TXT("tips") message:kJL_TXT("current_update_task") preferredStyle:UIAlertControllerStyleAlert];
    ...
    [alert addAction:[UIAlertAction actionWithTitle:kJL_TXT("confirm") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [[LoopUpdateManager share] cleanList];
        self.hidden = YES;
    }]];
}

Source: AutoFinishView.m

错误终止 TipsFinishView

升级过程发生错误时,错误弹窗直接终止整个自动化流程:

kJLLog(JLLOG_ERROR, @"发生错误导致关闭自动化升级!!!");
[[LoopUpdateManager share] cleanList];

Source: TipsFinishView.m

进度弹窗 TipsProgressView

OTA 进度推进过程中,若处于自动化测试模式且存在批量队列,则触发自动重连视图:

if([ToolsHelper isAutoTestOta]){
    if([[LoopUpdateManager share] shouldLoopUpdate]){
        [self.autoView reConnectByAutoUpdate];
    }
}

Source: TipsProgressView.m

设计意图:UI 层只做"查询状态 + 触发动作",所有队列状态以 LoopUpdateManager 单例为唯一事实来源,保证多个弹窗视图之间不会出现状态漂移。

用法示例

示例一:开启自动化测试并配置循环次数

在开始自动升级前,通过 ToolsHelper 打开自动化测试开关并设置升级轮数:

[ToolsHelper setAutoTestOta:YES];          // 打开自动化测试模式
[ToolsHelper setAutoTestOtaNumber:10];     // 连续升级 10 次
[ToolsHelper setFaultTolerant:YES];        // 开启容错
[ToolsHelper setFaultTolerantTimes:3];     // 最多容忍 3 次失败

Source: ToolsHelper.m

示例二:启动批量升级队列

传入固件文件名列表(文件需已放入 Documents/upgrade 目录),队列管理器会按配置次数展开并立即开始第一轮:

NSArray *fileNames = @[@"app_update_1.0.0.ufw", @"app_update_1.0.1.ufw"];
BOOL started = [[LoopUpdateManager share] startLoopUpdate:fileNames];
if (started) {
    // 队列已启动,等待升级完成回调推进下一轮
}

Source: LoopUpdateManager.m

示例三:判断并终止批量流程

if ([[LoopUpdateManager share] shouldLoopUpdate]) {
    // 仍有多轮待升级,提示用户
    [[LoopUpdateManager share] cleanList];  // 用户确认后终止
}

Source: LoopUpdateManager.m

配置项

配置键类型默认值说明
AutoTestOtaBOOLNO自动化测试总开关,isAutoTestOta 为 YES 时队列才会推进与自动重连
AutoTestOtaNumberNSInteger1循环升级次数,0 时 startLoopUpdate: 直接返回 NO 不启动
fault_tolerantBOOL未设置(读为 NO)容错开关;开启后仅当失败次数未达上限且状态为 Prepare/Finish 时才推进队列
fault_tolerant_timesNSInteger未设置(读为 0)容错次数上限,实际生效条件为 getFaultTolerantTimes > failedNumber

所有配置通过 DFTools(NSUserDefaults 封装)持久化,App 重启后保留。getAutoTestOtaNumber 在未设置时默认返回 1,保证单次升级场景不受影响。

API 参考

LoopUpdateManager(单例)

+ (instancetype)share

  • 描述:获取全局唯一队列管理器实例(dispatch_once 保证线程安全)。
  • 返回:LoopUpdateManager *。

- (BOOL)shouldLoopUpdate

  • 描述:判断是否处于批量升级模式。
  • 返回:updatePath.count > 1 时为 YES,否则 NO。

- (void)cleanList

  • 描述:清空升级队列,立即终止自动化流程(用户取消或错误终止时调用)。

- (BOOL)startLoopUpdate:(NSArray *)filePaths

  • 参数:filePaths(NSArray<NSString>*)固件文件名列表,文件需位于 Documents/upgrade 目录。
  • 行为:队列为空时按 getAutoTestOtaNumber 展开队列、记录 reConnectUUID、重置完成计数,并立即升级队首文件;队列非空时直接推进 toNextUpdate。
  • 返回:启动成功 YES;配置次数为 0 时返回 NO。
  • 状态副作用:设置 info.name(当前文件名)、info.nowIndexStr(形如 "1/N" 的进度文案)。

- (BOOL)toNextUpdate

  • 行为:按容错规则移除队首;若 isAutoTestOta 且队列仍有剩余,则开始下一轮升级并更新进度。
  • 返回:成功推进并启动下一轮 YES;队列为空或非自动测试模式返回 NO。
  • 异常:removeObjectAtIndex:0 越界时由 @try/@catch 捕获并返回 NO。

- (void)startLoopOta

  • 行为:自动化测试模式下自动重连设备;isSupportHID 为 YES 时调用 findHid:,否则调用 connectPeripheralWithUUID:,均使用 reConnectUUID 作为目标。

- (void)startLoopOta 依赖的 JLBleManager 接口(本页仅说明调用关系):

  • otaFuncWithFilePath: — 以指定固件路径开始一次 OTA 升级;
  • connectPeripheralWithUUID: — 按 UUID 重连普通 BLE 设备;
  • findHid: — 按 UUID 重连支持 HID 的设备。

ToolsHelper(类方法)

+ (BOOL)isAutoTestOta / + (void)setAutoTestOta:(BOOL)status

  • 读写键 AutoTestOta,未设置时默认 NO。

+ (NSInteger)getAutoTestOtaNumber / + (void)setAutoTestOtaNumber:(NSInteger)number

  • 读写键 AutoTestOtaNumber,未设置时默认 1。

+ (BOOL)getFaultTolerant / + (void)setFaultTolerant:(BOOL)status

  • 读写键 fault_tolerant。

+ (NSInteger)getFaultTolerantTimes / + (void)setFaultTolerantTimes:(NSInteger)number

  • 读写键 fault_tolerant_times。

LoopInfo(进度模型)

LoopInfo 为轻量数据模型(定义于 LoopUpdateManager.h,实现为空壳类),由队列管理器在运行时写入 name(当前固件文件名)与 nowIndexStr("当前轮/总轮数")供 UI 展示;LoopUpdateManager 自身维护 finishNumber(已完成数)、failedNumber(失败数)、status(DeviceOtaStatusPrepare / DeviceOtaStatusFinish 等)与 reConnectUUID 等运行期状态。

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

空队列 / 越界

toNextUpdate 与 startLoopUpdate 均在 removeObjectAtIndex:0 处使用 @try/@catch 防御。队列为空时抛出 NSException,被捕获后记录日志"自动升级队列为空"并返回 NO,流程安全终止而非崩溃。这是对本机制最关键的边界保护——任何回调乱序(如升级完成后重复调用推进)都不会导致数组越界。

容错开关的两种行为

  • 容错开启:仅当 getFaultTolerantTimes > failedNumber 且 当前状态为 DeviceOtaStatusPrepare 或 DeviceOtaStatusFinish 时才弹出队首继续。这意味着一旦升级失败且状态落在其他分支(如失败态),队列会"卡住"停止推进,等待外部干预——这是有意设计,防止在设备异常时继续烧写造成变砖;
  • 容错关闭:无条件移除队首继续,即失败也照常进入下一轮,适合希望"跑完 N 轮"的压力测试场景。

状态机约束

推进操作依赖 _status 属于 DeviceOtaStatusPrepare/DeviceOtaStatusFinish 之一。状态由 JLBleManager 的 OTA 回调驱动,任何"升级中"状态下的推进都会被容错逻辑拦截,避免在传输中途切换固件。

并发与线程

LoopUpdateManager 单例本身不持有串行队列或锁,所有操作在调用线程(BLE 回调线程与 UI 线程)上直接执行。updatePath 的读写(cleanList 与 toNextUpdate 可能在不同线程触发)存在理论上的竞态:例如用户确认取消(UI 线程 cleanList)与升级完成回调(BLE 线程 toNextUpdate)同时发生时,可能出现"清空后又被追加/推进"的窗口。现有代码通过 @try/@catch 保证不崩溃,但未做显式同步;若需在生产中强化,应在 cleanList/toNextUpdate 上增加互斥。

设备重启失联

自动重连依赖 reConnectUUID。若设备重启后长时间未广播、或 UUID 因系统重置变化,findHid:/connectPeripheralWithUUID: 将无法回连,队列停留在等待重连状态——源码未包含超时重试或放弃逻辑,实际表现为自动化流程挂起,需人工介入。

性能与运维注意事项

  • 串行执行:多轮升级严格按队列顺序逐次执行,天然避免多设备/多线程并发烧写的资源竞争,但单设备总耗时 = 单轮耗时 × N,压测周期较长;
  • 队列内存:updatePath 仅保存文件路径字符串(非固件数据),N 很大时内存开销可忽略;固件数据由 JLBleManager 按轮读取;
  • 文件目录约定:固件文件必须预置于 Documents/upgrade 目录([DFFile listPath:NSDocumentDirectory MiddlePath:@"upgrade" File:path]),路径拼接在队列构建时完成;
  • 日志:关键节点(重连 UUID、队列剩余次数、错误终止)均通过 kJLLog 输出,可在真机日志中按 auto update count、toNextUpdate 剩余 关键字跟踪压测进度;
  • 配置持久化:循环次数与容错参数存于 NSUserDefaults,测试完成后应调用 setAutoTestOta:NO 关闭自动化开关,避免后续普通升级被意外带入批量模式。

扩展点

  • 新增推进策略:toNextUpdate 中的容错分支(getFaultTolerant 开关)即策略插槽,可按需扩展"失败重试次数""失败即终止"等规则;
  • 重连方式扩展:startLoopOta 根据 isSupportHID 二选一(HID/BLE),新协议设备的回连可在该方法中追加分支;
  • 进度模型:LoopInfo 目前仅承载名称与 nowIndexStr,可扩展添加剩余字节、耗时等字段供 UI 增强;
  • 触发入口:AutoDevicesViewController 是当前唯一启动入口,startLoopUpdate: 的 filePaths 参数使其可被其他页面复用,实现任意文件列表的批量升级。

测试覆盖情况

源码中未发现针对 LoopUpdateManager/ToolsHelper 的单元测试文件;自动化测试能力的验证主要依赖真机上的 AutoDevicesViewController 人工压测(配置次数后观察多轮升级)。@try/@catch 空队列防御、number == 0 提前返回等边界处理体现了对异常路径的显式考量,可作为后续补充 XCTest 用例的切入点(如空队列推进、容错开关分支、次数为 0 启动)。

Related Links

  • LoopUpdateManager.m 源码
  • ToolsHelper.m 源码
  • AutoDevicesViewController.m 源码
  • AutoFinishView.m 源码
  • TipsFinishView.m 源码
  • TipsProgressView.m 源码
  • 相关主题:OTA 单次升级流程(JLBleManager 的 otaFuncWithFilePath: 传输细节见 OTA 升级流程页面)、BLE 连接管理(见 JLBleManager 页面)、固件配置与升级包管理(见 UfwConfig 页面)
Prev
标准升级流程
Next
广播音箱升级