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

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

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

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

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

多设备 OTA 模型

多设备 OTA 模型是杰理 Android OTA SDK(otasdk)中用于管理"一台手机同时对多台广播盒设备执行固件升级"的完整状态机与消息机制,覆盖设备选择、固件分发、断线回连与结束清理的整个生命周期。

Purpose and Scope

本文档介绍 com.jieli.broadcastbox 包内"广播盒多设备升级"能力(catalog:demo-app.multi-device),包括:

  • 多设备 OTA 的核心状态模型 MultiOTAState 及其状态常量;
  • 状态流转过程中使用的消息模型(MultiOTAStart / MultiOTAWorking / MultiOTAReconnect / MultiOTAEnd / MultiOTAStop);
  • 设备列表的 UI 管理逻辑(UpgradeFragment 的选择与实时更新);
  • 多设备 OTA 处理器 MultiOTAProcessor 的集成入口(BroadcastBoxActivity)。

以下主题属于兄弟页面,不在本文展开:单设备 OTA 升级流程、BLE 连接管理(BleManager)、升级文件选择(UpgradeFilePickerAdapter)、以及 OTA 文件监听(OtaFileObserver)。

Overview

在杰理的 OTA 应用场景中,一台 Android 设备(手机)可以作为上位机,同时连接多台"广播盒"(BroadcastBox)设备进行固件升级。与单设备 OTA 相比,多设备模型需要额外解决三个问题:

  1. 状态机统一:每台设备的升级进度不同步,需要以统一的 MultiOTAState 状态常量描述整个批处理的宏观阶段(空闲 → 启动 → 工作中 → 回连 → 结束);
  2. 消息解耦:升级过程中的每个阶段(开始、进行中、断线回连、结束、停止)都封装为独立的消息类,便于 UI 层通过事件机制感知状态变化;
  3. 选择列表管理:用户在多台已连接设备间勾选,UpgradeFragment 维护一份"已选择设备列表",并随连接状态实时增删,保证升级开始前列表与真实连接一致。

MultiOTAState 是这一模型的抽象基类,所有阶段消息都继承自它,通过 getState() 暴露当前所处阶段。整个模型由 MultiOTAProcessor 驱动,该处理器由 BroadcastBoxActivity 在广播盒升级场景中创建并调用。

Architecture

下图展示了多设备 OTA 模型的整体架构:UI 层负责设备勾选与列表维护,核心处理器驱动状态机,状态机通过消息模型与每台 BLE 设备交互。

flowchart TD
    subgraph sg_UI["UI 层 (com.jieli.broadcastbox)"]
        Activity["BroadcastBoxActivity"]
        Fragment["UpgradeFragment"]
        VM["UpgradeViewModel<br/>SelectedDeviceList"]
        Adapter["UpgradeDeviceAdapter"]
    end

    subgraph sg_Core["多设备 OTA 核心"]
        Processor["MultiOTAProcessor"]
        StateModel["MultiOTAState<br/>(状态常量抽象基类)"]
        Models["MultiOTA 阶段消息模型"]
    end

    subgraph sg_Device["设备层"]
        BLE1["BLE 设备 1 (广播盒)"]
        BLE2["BLE 设备 2 (广播盒)"]
        BLE3["BLE 设备 N (广播盒)"]
    end

    Activity -->|"创建/调用"| Processor
    Fragment -->|"勾选/取消"| VM
    Fragment -->|"展示"| Adapter
    VM -->|"待升级设备列表"| Processor
    Processor -->|"继承/实例化"| StateModel
    Processor -->|"阶段切换"| Models
    Models -->|"固件传输"| BLE1
    Models -->|"固件传输"| BLE2
    Models -->|"固件传输"| BLE3

架构说明

  • BroadcastBoxActivity:广播盒升级场景的 Activity 入口,导入并持有 MultiOTAProcessor(见 BroadcastBoxActivity.java),是多设备升级的驱动起点。
  • UpgradeFragment:负责渲染设备列表、维护用户勾选结果,并在连接状态变化时(MSG_UPDATE_DEVICE_LIST 消息)重建实时列表。
  • MultiOTAState:抽象状态基类,定义了批处理升级的五个宏观阶段常量,是全部阶段消息的父类。
  • model/ota 消息模型:MultiOTAStart、MultiOTAWorking、MultiOTAReconnect、MultiOTAEnd、MultiOTAStop 五个具体阶段消息,分别对应升级的启动、工作、回连、结束、停止动作。
  • BLE 设备层:多台广播盒设备,处理器逐个推送固件,断线时进入回连状态。

状态模型:MultiOTAState

MultiOTAState 是多设备 OTA 状态机的抽象基类,位于 com.jieli.broadcastbox.model.ota 包。它只做两件事:定义状态常量、持有并暴露当前状态值。这种设计把"状态定义"与"状态行为"分离——具体阶段消息(如 MultiOTAWorking)通过继承获得 state 字段,同时可以携带各自阶段的专属数据。

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

状态常量语义

常量值含义对应阶段消息
STATE_IDLE0空闲,无升级任务—
STATE_START1升级启动,准备推送固件MultiOTAStart
STATE_WORKING2升级进行中,正在传输固件MultiOTAWorking
STATE_OTA_RECONNECT3设备断开,进入回连流程MultiOTAReconnect
STATE_OTA_STOP4升级结束(成功或失败)MultiOTAEnd / MultiOTAStop

设计意图:状态常量使用 int 而非枚举,是为了在 Handler/Message 跨线程传递时保持轻量(Message.what 天然是 int),并且允许扩展方在不修改基类的前提下新增自定义阶段值(只要不与现有值冲突)。

状态机流转

多设备 OTA 的批处理生命周期可以用状态机表达:升级从空闲进入启动,随后处于工作中;任一设备断线时进入回连状态,回连成功后回到工作状态继续传输;全部完成后进入结束/停止状态。

stateDiagram-v2
    [*] --> STATE_IDLE: 初始化
    STATE_IDLE --> STATE_START: 用户点击升级
    STATE_START --> STATE_WORKING: 开始推送固件
    STATE_WORKING --> STATE_OTA_RECONNECT: 设备断开
    STATE_OTA_RECONNECT --> STATE_WORKING: 回连成功
    STATE_OTA_RECONNECT --> STATE_OTA_STOP: 回连失败/超时
    STATE_WORKING --> STATE_OTA_STOP: 全部设备升级完成
    STATE_OTA_STOP --> [*]: 释放资源

该状态机由 MultiOTAProcessor 驱动。本次文档探索中,MultiOTAProcessor 的实现细节未能在仓库路径 com/jieli/broadcastbox/multidevice/MultiOTAProcessor.java 下直接读取到(该文件未被当前目录树收录或位于其他模块),但其存在与职责已由 BroadcastBoxActivity.java 的导入语句证实;上述状态流转是依据 MultiOTAState 常量定义与阶段消息命名归纳的模型语义。

阶段消息模型

model/ota 包内除基类 MultiOTAState 外,还有五个具体阶段类,共同构成消息模型族:

classDiagram
    class MultiOTAState {
        <<abstract>>
        +STATE_IDLE: int = 0
        +STATE_START: int = 1
        +STATE_WORKING: int = 2
        +STATE_OTA_RECONNECT: int = 3
        +STATE_OTA_STOP: int = 4
        -state: int
        +getState() int
    }

    class MultiOTAStart {
    }

    class MultiOTAWorking {
    }

    class MultiOTAReconnect {
    }

    class MultiOTAEnd {
    }

    class MultiOTAStop {
    }

    MultiOTAState <|-- MultiOTAStart
    MultiOTAState <|-- MultiOTAWorking
    MultiOTAState <|-- MultiOTAReconnect
    MultiOTAState <|-- MultiOTAEnd
    MultiOTAState <|-- MultiOTAStop
消息类源文件阶段典型触发时机
MultiOTAStartmodel/ota/MultiOTAStart.java启动用户确认升级,处理器开始遍历设备
MultiOTAWorkingmodel/ota/MultiOTAWorking.java工作中固件分包传输、进度上报
MultiOTAReconnectmodel/ota/MultiOTAReconnect.java回连设备在升级中途断开 BLE 连接
MultiOTAEndmodel/ota/MultiOTAEnd.java结束全部设备升级成功
MultiOTAStopmodel/ota/MultiOTAStop.java停止升级失败、用户取消或异常终止

这种"每阶段一个消息类"的设计使得 MultiOTAProcessor 可以通过统一的 MultiOTAState 引用向 UI 层派发阶段事件,UI 层只需判断 getState() 的取值即可决定界面表现,无需感知具体子类,实现了处理逻辑与表现逻辑的解耦。

设备列表管理(UI 层)

多设备 OTA 的用户入口是 UpgradeFragment。它通过 UpgradeViewModel 维护两份列表:已连接设备列表(getConnectedBleDevices())和已选择设备列表(getSelectedDeviceList())。已选择列表是升级批次的输入,处理器将以此为依据逐个推送固件。

勾选 / 取消勾选

用户在设备列表中勾选设备时,UpgradeFragment 会维护 SelectedDeviceList 的增删:

if (!state) {
    if (!viewModel.getSelectedDeviceList().contains(boxInfo)) {
        viewModel.getSelectedDeviceList().add(boxInfo);
    }
} else {
    viewModel.getSelectedDeviceList().remove(boxInfo);
}

Source: UpgradeFragment.java

设计意图:先做 contains 判重再 add,避免同一设备被重复加入升级批次;取消勾选时直接 remove,保持列表与用户意图严格一致。

连接列表实时同步

升级开始前必须保证"已选择列表 ⊆ 已连接列表"。MSG_UPDATE_DEVICE_LIST 消息触发 updateConnectedDeviceList(),将已连接设备与缓存选择做交集合并,重建实时列表:

private void updateConnectedDeviceList() {
    List<BroadcastBoxInfo> connectedDevices = viewModel.getConnectedBleDevices(); //已连接设备列表
    List<BroadcastBoxInfo> cacheSelected = viewModel.getSelectedDeviceList();      //缓存的选择设备列表
    List<BroadcastBoxInfo> realTimeList = new ArrayList<>();  //实时更新列表
    JL_Log.i(TAG, "updateConnectedDeviceList >> connectedDevices = " + connectedDevices.size() + ", cacheSelected = " + cacheSelected.size());
    boolean isEmpty = connectedDevices.isEmpty();
    // ... 合并逻辑:保留仍处于连接状态的选择设备 ...
    JL_Log.i(TAG, "updateConnectedDeviceList >> realTimeList = " + realTimeList.size());
    adapter.setList(connectedDevices);
    viewModel.getSelectedDeviceList().clear();
    viewModel.getSelectedDeviceList().addAll(realTimeList);
}

Source: UpgradeFragment.java

这段代码体现了多设备模型的关键容错策略:已断开连接的设备会被自动从选择列表中剔除(realTimeList 只保留仍连接的选择设备,随后清空并重建 SelectedDeviceList)。这样即使用户在勾选后有一台设备掉线,升级批次也不会把失效设备纳入,从源头避免升级失败。

升级按钮的可用性同样依赖选择列表:UpgradeFragment 会遍历 getSelectedDeviceList(),只要存在 getSelectFile() == null 的设备(未选择升级文件)就视为未就绪,阻止开始升级:

boolean ready = true;
for (BroadcastBoxInfo info : viewModel.getSelectedDeviceList()) {
    if (info.getSelectFile() == null) {
        ready = false;
        break;
    }
}

Source: UpgradeFragment.java

核心流程

一次典型的多设备 OTA 升级,从用户操作到设备固件传输,完整时序如下:

sequenceDiagram
    participant U as 用户
    participant F as UpgradeFragment
    participant VM as UpgradeViewModel
    participant P as MultiOTAProcessor
    participant D1 as 广播盒设备 A
    participant D2 as 广播盒设备 B

    U->>F: 勾选设备 A、B
    F->>VM: add/remove SelectedDeviceList
    VM-->>F: 已选择设备列表
    U->>F: 点击"开始升级"
    F->>F: 校验每台设备已选升级文件
    F->>P: 启动多设备 OTA (STATE_START)
    P->>D1: 推送固件 (STATE_WORKING)
    D1-->>P: 进度上报
    P->>D2: 推送固件 (STATE_WORKING)
    D2--xP: 连接断开
    P->>P: 进入回连 (STATE_OTA_RECONNECT)
    P->>D2: 尝试重连
    D2-->>P: 回连成功,继续传输
    P-->>F: 全部完成 (STATE_OTA_STOP)
    F->>U: 展示升级结果

流程要点

  1. 入口:BroadcastBoxActivity 持有 MultiOTAProcessor(BroadcastBoxActivity.java#L15),当用户从广播盒设备列表进入升级页时,UpgradeFragment 负责收集选择结果。
  2. 预检:处理器启动前,UI 层确认每台选择设备都绑定了升级文件(getSelectFile() 非空),防止"空文件升级"。
  3. 逐台分发:处理器按 STATE_WORKING 逐台推送固件;多台设备共享同一套状态常量,但各设备进度独立上报。
  4. 断线回连:任一台设备中断时状态切到 STATE_OTA_RECONNECT,由 MultiOTAReconnect 消息承载回连逻辑;回连成功则回到 STATE_WORKING 续传。
  5. 收尾:全部设备完成进入 STATE_OTA_STOP;MultiOTAEnd / MultiOTAStop 分别对应成功与异常终止两种结局。

API Reference

MultiOTAState(int state)

多设备 OTA 阶段消息的抽象基类构造方法。

参数:

  • state (int):阶段状态值,取值必须是 STATE_IDLE、STATE_START、STATE_WORKING、STATE_OTA_RECONNECT、STATE_OTA_STOP 之一(或扩展的自定义值)。

说明: 子类构造时传入自身所属阶段,基类保存为 final 字段,阶段一旦创建不可变更。

Source: MultiOTAState.java

int getState()

返回当前阶段状态值。

返回: int,值为上述五个常量之一。UI 层通过该值决定界面表现(如显示"升级中""回连中""完成")。

Source: MultiOTAState.java

状态常量表

常量值说明
MultiOTAState.STATE_IDLE0空闲
MultiOTAState.STATE_START1启动
MultiOTAState.STATE_WORKING2工作中
MultiOTAState.STATE_OTA_RECONNECT3回连
MultiOTAState.STATE_OTA_STOP4结束

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

断线剔除(已连接列表变化)

updateConnectedDeviceList() 只在 MSG_UPDATE_DEVICE_LIST 消息到达时重建列表。若设备在两次消息之间掉线,SelectedDeviceList 中会短暂残留失效设备;但升级前的"文件就绪校验"与重建逻辑会在下一次消息到达时将其剔除,因此最终批次是收敛的。

空文件升级防护

升级触发前逐台检查 getSelectFile() == null,任何一台设备未选文件即阻止整批启动。这是"全有或全无"的批次语义:不会出现部分设备升级、部分设备因缺文件失败的情况。

重复勾选防护

add 前 contains 判重,杜绝同一 BroadcastBoxInfo 被重复加入批次;remove 时不存在则静默忽略,不会抛异常。

回连失败

STATE_OTA_RECONNECT 若回连失败或超时,状态机进入 STATE_OTA_STOP(异常终止路径,对应 MultiOTAStop)。回连窗口与重试次数由 MultiOTAProcessor 内部策略决定(本次探索未读取到处理器实现细节,此处依据状态机常量语义归纳)。

并发与线程模型

MSG_UPDATE_DEVICE_LIST 通过 Handler/Message(Message.what 与状态常量同为 int,风格一致)在 UI 线程投递,列表增删(add/remove/clear/addAll)均在主线程串行执行,不存在并发修改问题。BLE 传输的回调若来自工作线程,需要投递回主线程再触碰列表——这是 MultiOTAState 使用 int 常量的原因之一:与 Message 机制天然兼容。

扩展点

  • 自定义阶段消息:继承 MultiOTAState 并选用未被占用的 int 值,即可扩展新的批处理阶段(例如"等待用户确认"),无需修改基类。
  • 消息族扩展:在 model/ota 包内仿照 MultiOTAStart / MultiOTAWorking 新增消息类,处理器与 UI 层按 getState() 分发,新增阶段对既有逻辑透明。
  • 列表策略替换:UpgradeFragment 的勾选与实时同步逻辑集中在 updateConnectedDeviceList(),若需不同容错策略(如保留断线设备并标记),可在此方法内替换合并算法。

性能与运维

  • 列表规模:多设备批次直接受 BLE 连接数限制(通常个位数到十几台),SelectedDeviceList 的线性 contains 判重开销可忽略。
  • 日志可观测性:updateConnectedDeviceList() 通过 JL_Log.i 输出连接设备数与缓存选择数,运维时可据此判断"用户选择了 N 台、实际在线 M 台"的差异,快速定位批次被裁剪的原因。
  • 资源释放:STATE_OTA_STOP 是终态,处理器应在该状态下释放各设备的传输会话;UI 层在批次结束后重建列表(clear() + addAll()),避免陈旧引用。

Related Links

  • UpgradeFragment.java — 设备列表选择与实时同步的 UI 实现
  • BroadcastBoxActivity.java — 多设备 OTA 处理器集成入口
  • MultiOTAState.java — 状态模型基类
  • model/ota/ 包:MultiOTAStart.java、MultiOTAWorking.java、MultiOTAReconnect.java、MultiOTAEnd.java、MultiOTAStop.java — 阶段消息模型
  • 兄弟页面:单设备 OTA 升级流程、BLE 连接管理、升级文件选择与 OTA 文件监听
Prev
文件选择与升级界面