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

    • 杰理 OTA SDK 项目简介
    • 快速开始与接入指南
    • 工程结构与发布物
  • 核心库与依赖

    • OTA 核心库集成
    • 版本历史与更新说明
  • SDK 工具层

    • OTA 参数配置
    • 蓝牙扫描与连接管理
    • BLE 通道与事件回调
    • OTA 升级流程与状态模型
    • 固件文件管理与监听
  • 演示应用

    • 演示应用架构与主界面
    • 设备发现与连接界面
    • 文件选择与升级界面
    • 多设备 OTA 模型

OTA 升级流程与状态模型

本文档介绍 Android-JL_OTA SDK 中 OTA 升级的完整生命周期:从单设备 UpgradeInfo 状态机到多设备 MultiOTAState 状态体系,以及 UI 层如何通过观察者模式驱动升级进度展示、回连与结束处理。

Purpose and Scope

本页聚焦于 OTA 升级流程与状态模型,涵盖:

  • 单设备升级状态机(UpgradeInfo 的 STATE_STOP / STATE_WORKING / STATE_RECONNECT)
  • 多设备 OTA 状态体系(抽象基类 MultiOTAState 及其五个事件子类)
  • 升级类型(bootloader 与 fw 固件)与进度、错误信息的承载方式
  • UI 层(DialogUpgradeDevice、UpgradeProgressAdapter)如何订阅状态并驱动界面

以下内容属于兄弟页面,不在本页展开:BLE 连接管理与数据发送(tool/ota/ble/BleManager、SendBleDataThread)、SPP 串口连接(tool/ota/spp/ConnectionSppThread)、固件文件解析与校验、升级文件选择(DialogUpgradeFilePicker)。如需了解这些底层传输细节,请参阅对应页面。

Overview

OTA(Over-The-Air)升级是嵌入式设备(如蓝牙音箱、耳机)固件更新的核心能力。SDK 将升级过程抽象为可观察的状态机:

  • 单设备视角:UpgradeInfo 是单个设备升级的"状态快照",记录设备地址、固件文件名、进度(0~100)、升级类型、当前状态、错误码与错误消息。UI 的进度列表直接渲染该对象。
  • 多设备视角:MultiOTAState 是抽象基类,配合 LiveData(multiOtaStateMLD)把"开始 / 传输中 / 回连 / 结束 / 停止"等事件推送给 UI。广播盒(BroadcastBox)场景下多个设备并行升级时,每个事件都携带设备维度信息。

状态模型的设计意图在于:把传输层(BLE/SPP)与 UI 解耦——传输层只负责上报状态事件,UI 只依赖状态常量与数据字段渲染,二者之间通过 ViewModel + LiveData 桥接,任何一侧的变更都不会牵动另一侧。

Architecture

flowchart TD
    subgraph sg_UI["UI 层 (com.jieli.broadcastbox)"]
        Activity["BroadcastBoxActivity"]
        Fragment["UpgradeFragment"]
        Dialog["DialogUpgradeDevice"]
        Adapter["UpgradeProgressAdapter"]
    end

    subgraph sg_Model["模型层 (model)"]
        UpgradeInfo["UpgradeInfo"]
        MultiState["MultiOTAState (abstract)"]
        Start["MultiOTAStart"]
        Working["MultiOTAWorking"]
        Reconnect["MultiOTAReconnect"]
        End["MultiOTAEnd"]
        Stop["MultiOTAStop"]
    end

    subgraph sg_ViewModel["ViewModel"]
        MLD["multiOtaStateMLD (LiveData)"]
    end

    subgraph sg_SDK["SDK 传输层 (tool/ota)"]
        Ble["BleManager / SendBleDataThread"]
        Spp["ConnectionSppThread"]
    end

    Dialog --> MLD
    MLD --> Dialog
    Dialog --> UpgradeInfo
    Adapter --> UpgradeInfo
    Activity --> Fragment
    Fragment --> Dialog
    Dialog --> Ble
    Ble --> Spp
    MultiState <|-- Start
    MultiState <|-- Working
    MultiState <|-- Reconnect
    MultiState <|-- End
    MultiState <|-- Stop
    MLD -. "携带事件对象" .-> MultiState

各角色说明:

  • UpgradeInfo:单设备升级状态载体,以 deviceAddress 作为唯一标识(equals/hashCode 均基于该字段),由 DialogUpgradeDevice 在收到 STATE_START 时创建并写入升级列表。
  • MultiOTAState 及五个子类:多设备升级的事件模型。DialogUpgradeDevice 在 multiOtaStateMLD.observe(...) 中按 getState() 分发并强转为对应子类,从而取出各自字段。
  • UpgradeProgressAdapter:升级进度列表的适配器,根据 UpgradeInfo.getState() 渲染不同文案(如传输中、重连中)。
  • ViewModel 的 multiOtaStateMLD:状态事件的统一出口,将 SDK 传输层的回调转化为 UI 可观察的 LiveData 流。

状态模型详解

单设备状态:UpgradeInfo

UpgradeInfo.java 定义了单设备升级的三态常量与全部数据字段:

public static final int STATE_STOP = 0;
public static final int STATE_WORKING = 1;
public static final int STATE_RECONNECT = 2;

private final String deviceAddress;  //upgrade device mac
private String deviceName;// upgrade device name
private String filename;// upgrade file
private int progress = 0;// ota progress
private int upgradeType = 0;// ota type: 0->bootloader, 1->fw upgrade
private int state = STATE_STOP;// OTA state
private int error;// OTA error
private String message; //error message

Source: UpgradeInfo.java

状态常量值含义
STATE_STOP0未开始/已停止,默认初始状态
STATE_WORKING1升级传输进行中
STATE_RECONNECT2设备断连,正在回连(如双模设备切换 BLE/SPP 通道)

字段设计要点:

  • deviceAddress 为 final 且唯一:构造时即固定,equals/hashCode 只比较 deviceAddress。这意味着升级列表按 MAC 去重,同一设备重复上报不会产生重复条目。
  • upgradeType:0 表示 bootloader 升级,1 表示固件(fw)升级,决定底层指令协议分支。
  • error + message:错误码与可读错误消息分离存储,UI 可同时展示数字码与文案。
  • Fluent setter 风格:所有 setter 返回 this,便于链式构造(见下文 Usage Examples)。

多设备状态:MultiOTAState 事件体系

MultiOTAState.java 是广播盒多设备升级的抽象状态基类:

public abstract class MultiOTAState {
    public static final int STATE_IDLE = 0;
    public static final int STATE_START = 1;
    public static final int STATE_WORKING = 2;
    /**
     * 回连状态
     */
    public static final int STATE_OTA_RECONNECT = 3;
    /**
     * ota结束
     */
    public static final int STATE_OTA_STOP = 4;

    private final int state;

    public MultiOTAState(int state) {
        this.state = state;
    }

    public int getState() {
        return state;
    }
}

Source: MultiOTAState.java

与单设备 UpgradeInfo 的"三态快照"不同,多设备体系采用事件对象模式:基类只持有不可变的 state 常量,每个子类携带该状态特有的载荷。DialogUpgradeDevice 的观察回调中按状态强转取数,例如 STATE_IDLE 强转为 MultiOTAEnd、STATE_START 强转为 MultiOTAStart、STATE_WORKING 强转为 MultiOTAWorking:

switch (multiOTAState.getState()) {
    case MultiOTAState.STATE_IDLE: {
        MultiOTAEnd otaEnd = (MultiOTAEnd) multiOTAState;
        ...
    }
    case MultiOTAState.STATE_START: {
        MultiOTAStart otaStart = (MultiOTAStart) multiOTAState;
        ...
    }
    case MultiOTAState.STATE_WORKING: {
        MultiOTAWorking otaWorking = (MultiOTAWorking) multiOTAState;
        ...
    }
}

Source: DialogUpgradeDevice.java

子类对应状态常量事件含义
MultiOTAStartSTATE_START (1)设备开始升级;UI 据此创建 UpgradeInfo 条目并入列表
MultiOTAWorkingSTATE_WORKING (2)固件数据正在传输,携带进度信息
MultiOTAReconnectSTATE_OTA_RECONNECT (3)传输中断,设备回连中
MultiOTAEndSTATE_IDLE (0)单台设备升级结束,回到空闲态
MultiOTAStopSTATE_OTA_STOP (4)升级被手动停止或异常终止

说明:MultiOTAEnd 对应 STATE_IDLE(升级结束后设备回到空闲),与 UpgradeInfo.STATE_STOP 的语义一致;子类内部字段在源码中未进一步展开,具体载荷以 SDK 回调为准。

状态转移总览

stateDiagram-v2
    [*] --> IDLE: 初始化
    IDLE --> START: 下发升级指令
    START --> WORKING: 固件传输开始
    WORKING --> RECONNECT: 链路断开/切换
    RECONNECT --> WORKING: 回连成功,续传
    RECONNECT --> STOP: 回连失败/超时
    WORKING --> END: 传输完成 100%
    END --> IDLE: 设备复位重启
    STOP --> [*]

状态转移规则(依据源码可验证的部分):

  1. 初始为 IDLE;用户选择固件文件并发起升级后进入 START。
  2. START 阶段 UI 创建 UpgradeInfo 并置为 STATE_WORKING(DialogUpgradeDevice 第 188 行 setState(UpgradeInfo.STATE_WORKING))。
  3. 传输中若底层链路断开(如 BLE 与 SPP 双模切换),进入 STATE_RECONNECT;UpgradeProgressAdapter 对 STATE_RECONNECT 渲染"重连中"文案。
  4. 回连成功回到 WORKING 续传;回连失败或升级完成则进入结束/停止分支。
  5. 升级结束(STATE_OTA_STOP / STATE_IDLE)后,MultiOTAEnd 事件通知 UI 清理该设备的升级条目。

核心流程:从发起升级到界面展示

下图描述了广播盒场景下一次 OTA 升级从用户操作到 UI 刷新的完整时序(基于 DialogUpgradeDevice 的观察回调与 UpgradeInfo 列表渲染逻辑):

sequenceDiagram
    participant U as 用户
    participant D as DialogUpgradeDevice
    participant VM as ViewModel
    participant SDK as SDK (BleManager/Spp)
    participant DEV as 目标设备
    participant L as 升级列表+UpgradeProgressAdapter

    U->>D: 选择固件文件,点击升级
    D->>SDK: 下发升级指令 (OTA_START)
    SDK->>DEV: 发送启动帧
    DEV-->>SDK: 应答
    SDK-->>VM: 上报 MultiOTAStart (STATE_START)
    VM-->>D: multiOtaStateMLD 回调
    D->>D: 强转 MultiOTAStart,创建 UpgradeInfo(STATE_WORKING)
    D->>L: add(upgradeInfo)
    L-->>U: 列表出现新设备条目

    loop 固件分包传输
        SDK->>DEV: 数据帧 (progress++)
        DEV-->>SDK: 确认
        SDK-->>VM: 上报 MultiOTAWorking (STATE_WORKING)
        VM-->>D: 回调
        D->>L: 更新 progress
    end

    alt 链路断开
        SDK-->>VM: 上报 MultiOTAReconnect (STATE_OTA_RECONNECT)
        VM-->>D: 回调
        D->>L: 置 UpgradeInfo.STATE_RECONNECT
        L-->>U: 显示"重连中"
        SDK->>DEV: 回连
        DEV-->>SDK: 回连成功
    end

    SDK-->>VM: 上报 MultiOTAEnd (STATE_IDLE / STATE_OTA_STOP)
    VM-->>D: 回调
    D->>L: 移除/结束条目
    L-->>U: 升级完成提示

关键控制流说明:

  1. 状态上报统一走 LiveData:SDK 传输层不直接触碰 UI,而是通过 ViewModel 的 multiOtaStateMLD 推送 MultiOTAState 事件;DialogUpgradeDevice 在 addObserver() 中 observe 该 LiveData 并 switch 分发(源码见 DialogUpgradeDevice.java)。
  2. 列表条目在 START 时创建、END 时清理:收到 STATE_START 事件才创建 UpgradeInfo,避免空跑条目;升级结束事件负责收尾。
  3. 进度与状态分离渲染:UpgradeProgressAdapter 按 info.getState() 分支渲染——STATE_WORKING 显示进度百分比,STATE_RECONNECT 显示重连文案(源码见 UpgradeProgressAdapter.java)。

Usage Examples

示例 1:链式构造 UpgradeInfo(单设备状态快照)

UpgradeInfo 的 setter 全部返回 this,UI 层在收到开始事件后可用链式写法快速组装条目,并将状态置为 WORKING:

UpgradeInfo upgradeInfo = new UpgradeInfo(device.getAddress())
        .setDeviceName(device.getName())
        .setFilename(info.getSelectFile().getName())
        .setState(UpgradeInfo.STATE_WORKING);
list.add(upgradeInfo);

Source: DialogUpgradeDevice.java

设计意图:deviceAddress 在构造时固定,equals/hashCode 仅依据它——因此即使同一设备被重复上报 START 事件,列表去重(List.contains / remove)也按 MAC 精确命中,不会出现重复行。

示例 2:按状态渲染升级列表

UpgradeProgressAdapter 根据 UpgradeInfo.getState() 决定文案,是状态模型的直接消费者:

switch (info.getState()) {
    case UpgradeInfo.STATE_WORKING: {
        String title;
        // 传输中:显示进度百分比
        ...
    }
    case UpgradeInfo.STATE_RECONNECT: {
        tvState.setText(String.format(Locale.getDefault(), "(%s)",
                getContext().getString(R.string.reconnecting)));
        ...
    }
}

Source: UpgradeProgressAdapter.java

示例 3:定义多设备状态事件子类

业务侧扩展新的事件类型时,继承 MultiOTAState 并在构造器传入对应状态常量即可,基类保证 state 不可变:

public class MultiOTAWorking extends MultiOTAState {
    public MultiOTAWorking() {
        super(STATE_WORKING);
    }
    // 携带进度、设备地址等载荷字段
}

说明:此示例依据基类 MultiOTAState.java 的构造函数约定(MultiOTAState(int state))推演而成;各子类的具体载荷字段请以 SDK 实际实现为准。

API Reference

UpgradeInfo(com.jieli.broadcastbox.model.UpgradeInfo)

单设备升级状态快照,升级列表的最小数据单元。源码:UpgradeInfo.java

方法签名说明
UpgradeInfo(String deviceAddress)构造器,deviceAddress(设备 MAC)为唯一必填项,创建后不可变
String getDeviceAddress()返回设备 MAC
UpgradeInfo setDeviceName(String)链式设置设备名
UpgradeInfo setFilename(String)链式设置固件文件名
UpgradeInfo setProgress(int)链式设置进度(0~100)
UpgradeInfo setUpgradeType(int)链式设置升级类型(0=bootloader,1=fw)
UpgradeInfo setState(int)链式设置状态(STATE_STOP/STATE_WORKING/STATE_RECONNECT)
UpgradeInfo setError(int)链式设置错误码
UpgradeInfo setMessage(String)链式设置错误消息
int getProgress() / int getUpgradeType() / int getState() / int getError() / String getMessage()对应字段读取器
boolean equals(Object) / int hashCode()仅基于 deviceAddress,用于列表去重与查找

Throws / 约束:

  • deviceAddress 不允许为空——它是 equals/hashCode 的唯一依据,空值会导致哈希运算抛 NullPointerException。
  • progress 的语义为 OTA 百分比(0~100),由上层 SDK 回调写入。

MultiOTAState(com.jieli.broadcastbox.model.ota.MultiOTAState)

多设备 OTA 事件的抽象基类。源码:MultiOTAState.java

成员说明
static final int STATE_IDLE = 0空闲(升级结束回落到该状态)
static final int STATE_START = 1开始事件
static final int STATE_WORKING = 2传输中事件
static final int STATE_OTA_RECONNECT = 3回连事件
static final int STATE_OTA_STOP = 4停止/结束事件
MultiOTAState(int state)受保护语义的构造器(抽象类),子类必须传入状态常量
int getState()返回不可变状态值

设计约束:

  • 基类为 abstract,禁止直接实例化;state 字段为 final,事件对象创建后状态不可变,保证 LiveData 分发过程中不会被篡改。
  • 状态常量同时承担"类型标签"职责:消费者按 getState() 强转对应子类取载荷,因此子类与状态常量必须一一对应,新增状态需同时扩展常量与子类。

Configuration Options

状态模型本身不读取配置文件,其"可配置项"体现在数据字段的取值约定上:

字段/常量可选值默认值说明
UpgradeInfo.upgradeType0 = bootloader 升级;1 = fw 固件升级0决定底层升级指令协议分支
UpgradeInfo.stateSTATE_STOP=0、STATE_WORKING=1、STATE_RECONNECT=2STATE_STOP单设备升级状态
UpgradeInfo.progress0~1000OTA 传输进度百分比
UpgradeInfo.error由 SDK 错误码表定义0升级失败时的错误码
MultiOTAState.stateSTATE_IDLE=0~STATE_OTA_STOP=4无(构造器必填)多设备事件类型标签

注:upgradeType 的 0/1 含义直接来自源码注释 // ota type: 0->bootloader, 1->fw upgrade(UpgradeInfo.java)。错误码的具体数值表在 SDK 传输层定义,本页不展开。

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

失败模式

失败场景状态模型中的体现处理路径
固件传输中断(BLE 掉线/切换 SPP)UpgradeInfo.STATE_RECONNECT / MultiOTAReconnectUI 显示"重连中";SDK 尝试回连后续传
回连失败/升级超时结束类事件(STATE_OTA_STOP)DialogUpgradeDevice 收到结束事件后清理条目
升级失败(校验错误、设备拒绝)UpgradeInfo.error + message错误码与可读文案分离存储,UI 可同时展示
重复上报开始事件equals/hashCode 基于 deviceAddress列表按 MAC 去重,避免重复条目

边界情况

  • 设备地址唯一性:UpgradeInfo 的相等性完全由 deviceAddress 决定,同一设备的不同升级会话(如先升 bootloader 再升 fw)会命中同一条目,UI 需自行区分 upgradeType 或覆盖旧条目。
  • STATE_IDLE 双关语义:MultiOTAState.STATE_IDLE 同时是初始状态和单台设备升级结束(MultiOTAEnd)的标签,消费者必须在"升级已开始"的上下文里收到它才表示结束,不能仅凭常量值判断。
  • 进度非单调保证:回连续传时 progress 可能从低于 100 的断点继续,UI 渲染不应假设进度严格递增。

并发与一致性

  • UI 线程约束:multiOtaStateMLD 是 LiveData,回调默认在主线程派发;DialogUpgradeDevice 在观察回调中直接操作升级列表与 UpgradeInfo,无需额外加锁。
  • 事件对象不可变:MultiOTAState.state 为 final,多线程共享事件对象时状态值不会漂移;可变载荷(如 UpgradeInfo.progress)由持有方(UI 列表)单线程更新。
  • 单线程列表更新:升级列表的增删(START 添加 / END 移除)都发生在 LiveData 回调(主线程)中,天然串行,不存在竞态窗口。

性能与运维

  • 状态事件频率:WORKING 事件随固件分包传输高频产生(每包或每几个包一次)。UI 层若直接在每个事件中刷新整个列表可能造成丢帧,建议在 UpgradeProgressAdapter 中仅更新变更行的视图(复用 ViewHolder + 按 deviceAddress 定位),这也是状态字段按设备隔离的原因。
  • 日志观测点:BroadcastBoxActivity 中通过 JL_Log 输出设备连接状态变化(如 >>>> deviceConnectionMLD >> ...),排查升级中断时可结合该日志与 error/message 字段定位链路层还是应用层问题。
  • 回连窗口:STATE_RECONNECT 期间 UI 处于等待态,回连是否成功由 SDK 传输层决定;建议运维侧为回连设置超时告警,避免界面长期停留在"重连中"。

扩展点

  1. 新增升级事件类型:继承 MultiOTAState,在基类中补充状态常量,并同步扩展 DialogUpgradeDevice 的 switch 分发与 UpgradeProgressAdapter 的渲染分支。
  2. 扩展升级类型:UpgradeInfo.upgradeType 目前只有 bootloader(0)/fw(1),新增类型需在 SDK 指令层与 UI 文案层协同扩展。
  3. 替换传输层:状态模型与 BLE/SPP 传输解耦(通过 ViewModel + LiveData 桥接),接入新的传输通道(如 TCP)时只需在 SDK 层把新通道的回调映射为 MultiOTAState 事件,UI 层零改动。

测试

SDK 的测试目录(otasdk/src/test)中的 OtaDemo.java 演示了完整调用链:以 BluetoothProfile.STATE_* 驱动连接状态切换,并将结果映射为 StateCode.CONNECTION_OK / CONNECTION_CONNECTING 等状态码。该示例验证了"连接状态 → 状态码 → UI"的映射模式,与升级状态模型遵循同一设计思想(OtaDemo.java)。针对 UpgradeInfo 的单元测试建议覆盖:equals/hashCode 的 MAC 唯一性、链式 setter 返回值、以及 STATE_WORKING/STATE_RECONNECT 分支渲染。

Related Links

  • UpgradeInfo.java(单设备状态模型)
  • MultiOTAState.java(多设备事件基类)
  • DialogUpgradeDevice.java(状态事件消费端)
  • UpgradeProgressAdapter.java(状态渲染适配器)
  • OtaDemo.java(调用链演示/测试示例)

相关页面导航:

  • 底层传输与连接管理:参见 BLE 管理器(tool/ota/ble/BleManager)与 SPP 连接(tool/ota/spp/ConnectionSppThread)相关文档。
  • 升级文件选择与校验:参见文件选择对话框(DialogUpgradeFilePicker)与固件解析相关文档。
  • 升级 UI 容器:参见 BroadcastBoxActivity / UpgradeFragment 页面。
Prev
BLE 通道与事件回调
Next
固件文件管理与监听