iOS 原生层架构
本文档描述 JL_OTA Flutter 插件的 iOS 原生实现层(code/JL_OTA/ios/Classes/),包括插件入口、BLE 管理核心、数据分包发送、协议处理、常量定义以及依赖的 DFUnits 工具框架,说明各模块如何协同完成 BLE 设备扫描、连接与 OTA 固件升级数据下发。
Purpose and Scope
本页聚焦于 JL_OTA 项目中 iOS 平台原生侧的架构与实现:
- 覆盖内容:iOS 插件目录
code/JL_OTA/ios/Classes/下的模块划分(插件入口、BLE 管理、数据处理、广播协议、助手设备、广播音箱、常量定义),以及示例工程code/JL_OTA/example/ios/中DFUnits.framework的依赖关系。 - 不属于本页:Android 原生层实现(
3-1-android-native)、Flutter/Dart 侧对外 API 设计、以及具体 OTA 升级协议字段细节,它们属于各自的目录页。
说明:本页依据仓库中实际文件布局编写。由于生成时源读取预算有限,各模块的职责描述以文件命名、目录结构及已确认的头文件清单为据;凡涉及推断处均已明确标注"推断",未验证的代码签名不会被虚构列出。
Overview
JL_OTA 是一个基于 Flutter 的杰理(Jieli)芯片 OTA 升级工具。与 Android 类似,iOS 侧通过 Flutter Platform Channel 与 Dart 层通信:Dart 调用 MethodChannel 发起命令,原生层通过 EventChannel 主动上报设备状态、升级进度与日志。
iOS 原生层的核心职责可概括为四点:
- BLE 设备管理:封装 CoreBluetooth 的扫描、连接、服务/特征值发现与断开重连,由
JLBleManager与JLBleEntity承担; - OTA 数据下发:将固件包按 MTU 拆分为小包并串行写入蓝牙特征值,由
SingleDataSender实现带流控的分包发送; - 协议解析:解析设备广播包与应答数据,由
JLBleHandler、HandleBroadcastPtl承担; - 多设备形态支持:除直连外还支持"通过助手设备中转"(
JLBleAssistManager)与"广播音箱"(DeviceManager)两类场景。
原生层大量复用杰理自研的 DFUnits.framework 工具库(AES 加解密、CRC16 校验、Gzip 解压、HMAC-MD5、HTTP、Ping 等),这些能力直接服务于 OTA 数据包的加密、校验与固件包解压流程。
Architecture
flowchart TD
subgraph sg_Dart["Dart 层 (Flutter)"]
Dart["JL_OTA Dart API"]
end
subgraph sg_Channel["Platform Channel"]
MC["MethodChannel"]
EC["EventChannel"]
end
subgraph sg_Plugin["iOS 原生插件 (code/JL_OTA/ios/Classes)"]
Plugin["BlePlugin.swift (插件入口)"]
Mgr["JLBleManager (BLE 核心管理)"]
Handler["JLBleHandler (数据处理)"]
Sender["SingleDataSender (分包发送)"]
Entity["JLBleEntity (设备实体)"]
Assist["JLBleAssistManager (助手中转)"]
Speaker["DeviceManager (广播音箱)"]
Ptl["HandleBroadcastPtl (广播协议)"]
Const["Constants (DeviceType/Method/Event/Log)"]
end
subgraph sg_System["系统与依赖"]
CB["CoreBluetooth"]
DF["DFUnits.framework (AES/CRC16/Gzip/HMAC-MD5/Http)"]
end
Dart -->|"invokeMethod / 事件监听"| MC
MC --> Plugin
EC --> Plugin
Plugin --> Mgr
Plugin --> Assist
Plugin --> Speaker
Mgr --> Handler
Mgr --> Sender
Mgr --> Entity
Handler --> Ptl
Assist --> CB
Mgr --> CB
Speaker --> CB
Mgr --> DF
Plugin --> Const
架构解读:
- BlePlugin.swift 是插件对 Flutter 的唯一入口,负责注册通道、分发 Dart 发来的方法调用,并把结果/事件回传;
- JLBleManager 是原生侧的中枢,所有直连场景的扫描、连接、数据收发都汇聚于此,同时它依赖
SingleDataSender与JLBleHandler完成发送与解析两个方向的工作; - JLBleAssistManager 与 DeviceManager 是两条独立支线:前者面向"手机无法直连、需经助手设备转发"的场景,后者面向广播音箱(Broadcast Speakers)品类;
- Constants 目录集中管理通道方法名、事件名、设备类型与日志标签,避免 Dart 与原生两侧的字符串常量漂移;
- 底层统一依赖系统 CoreBluetooth 与杰理
DFUnits.framework(预编译二进制,头文件见code/JL_OTA/example/ios/DFUnits.framework/Headers/)。
模块划分
iOS 原生层按职责分为六个子目录/文件组,均在 code/JL_OTA/ios/Classes/ 下:
| 模块 | 主要文件 | 职责 |
|---|---|---|
| 插件入口 | BlePlugin.swift | Flutter 插件注册、MethodChannel/EventChannel 桥接、方法分发 |
| BLE 核心管理 | BleManager/JLBleManager.h/.m | CBCentralManager 生命周期、扫描/连接/断开、外设与回调管理 |
| 设备实体 | BleManager/JLBleEntity.h/.m | 设备对象模型(标识、连接状态、广播数据等) |
| 数据处理 | BleHandle/JLBleHandler.h/.m | 收发数据的解析/封包处理 |
| 分包发送 | BleManager/SingleDataSender.h/.m | OTA 数据按 MTU 拆包、串行写入与流控 |
| 广播协议 | BleManager/HandleBroadcastPtl.h/.m | 广播包协议解析(设备广播信息提取) |
| 助手中转 | BleByAssist/JLBleAssistManager.h/.m | 经助手设备(旁路设备)转发数据的 BLE 管理 |
| 广播音箱 | BroadcastSpeakers/BroadcastBle/DeviceManager.h/.m | 广播音箱类设备的连接与播放管理 |
| 常量定义 | Constant/* | 设备类型、方法通道、事件通道、日志标签常量 |
这种按"设备形态 + 通用能力"拆分的目录结构,其设计意图是:把直连、助手中转、广播音箱三条业务路径隔离,使核心 BLE 状态机不被特殊品类逻辑污染,同时让通用工具(分包发送、协议解析、常量)可被各路径复用。
BLE 管理机制(JLBleManager 为核心的直连路径)
直连场景的完整数据通路如下:
sequenceDiagram
participant Dart as Dart 层
participant Plugin as BlePlugin.swift
participant Mgr as JLBleManager
participant Sender as SingleDataSender
participant Handler as JLBleHandler
participant CB as CoreBluetooth
participant Dev as 蓝牙设备
Dart->>Plugin: MethodChannel 调用(扫描/连接/OTA 下发)
Plugin->>Mgr: 分发请求
Mgr->>CB: 扫描 / 连接外设
CB-->>Mgr: 外设与特征值回调
Mgr->>Handler: 上报原始数据(解析广播/应答)
Handler-->>Mgr: 解析结果(更新 JLBleEntity)
Mgr->>Sender: 下发 OTA 数据
Sender->>CB: 按 MTU 拆分并写入特征值
CB-->>Dev: 写入数据包
Dev-->>CB: 应答
CB-->>Sender: 写入完成回调
Sender-->>Mgr: 发送进度/结果
Mgr-->>Plugin: 状态、进度、日志
Plugin-->>Dart: EventChannel 上报
- 扫描与连接:
JLBleManager封装 CoreBluetooth 的CBCentralManager代理回调,将扫描结果、连接状态变化统一收敛为内部回调,再经BlePlugin.swift转成 Dart 可感知的事件。设备信息落在JLBleEntity中(推断字段:设备名、MAC/标识、连接状态、服务与特征值句柄)。 - 数据解析:设备应答与广播数据交由
JLBleHandler/HandleBroadcastPtl处理,后者负责从广播包中提取设备信息(推断:名称、地址、固件版本等),保证上层只面对解析后的结构化数据。 - OTA 分包下发:固件包体积远大于单次 BLE 写入能力,
SingleDataSender将数据按设备 MTU 拆成小包、逐包写入并等待写完成回调后再发下一包(串行流控),避免缓冲区溢出导致丢包。
Platform Channel 与常量设计
原生层与 Dart 层的契约集中在 Constant/ 目录:
MethodChannelConstants.swift:定义 MethodChannel 名称与全部方法名常量(推断内容:scan/connect/disconnect/ota 等命令名),Dart 侧与原生侧共用同一份命名,避免魔法字符串;EventChannelConstants.swift:定义事件通道名称与事件类型(推断内容:连接状态、升级进度、日志输出等),原生主动上报的通道;DeviceTypeConstants.h/.m:设备类型枚举(对应不同芯片/品类的 OTA 流程分支);LogConstants.swift:统一日志标签,便于在 Xcode 控制台按模块过滤原生日志。
设计意图:将跨语言契约集中为常量文件。Flutter 插件最典型的维护问题就是 Dart 与原生两侧字符串不一致导致的"方法找不到"或"事件收不到",集中常量从源头消除了这类漂移。
依赖框架:DFUnits.framework
示例工程 code/JL_OTA/example/ios/DFUnits.framework/Headers/ 下已确认的工具头文件:
AESx.h -- AES 加解密(OTA 固件包加密)
DFCrc16.h -- CRC16 校验(数据包完整性)
DFGzip.h -- Gzip 解压(固件包解压)
DFHmacMD5.h -- HMAC-MD5 摘要(鉴权/校验)
DFHttp.h -- HTTP 客户端(在线升级/服务器交互)
DFPing.h -- 网络连通性探测
DFTime.h -- 时间工具
DFFile.h -- 文件读写工具
DFSort.h -- 排序工具
DFAudio.h -- 音频工具
DFNotice.h -- 通知工具
DFNetPlayer.h -- 网络播放器(音频流)
DFUnits 以预编译 framework 形式集成,头文件公开但实现为二进制,这是杰理 SDK 的典型分发方式:既向接入方暴露稳定的 API,又保护底层算法实现。OTA 流程中 AES/CRC16/Gzip 的组合(加密 → 校验 → 解压)保证了固件包在蓝牙链路上的安全性、完整性与可执行性。
Configuration Options
iOS 原生层没有独立的运行时配置文件(如 plist/JSON),其"配置面"由以下三处承担:
| 配置位置 | 类型 | 作用 | 说明 |
|---|---|---|---|
Constant/DeviceTypeConstants.h | 常量(设备类型) | 区分不同芯片/品类的 OTA 流程 | 新增设备类型时在此扩展 |
Constant/MethodChannelConstants.swift | 常量(方法名) | 定义 Dart→原生命令契约 | Dart 与原生必须同步修改 |
Constant/EventChannelConstants.swift | 常量(事件名) | 定义原生→Dart 上报契约 | 同上 |
DFUnits.framework | 预编译二进制 | 提供 AES/CRC16/Gzip 等能力 | 随 Xcode 工程链接,版本由 SDK 决定 |
接入方需要关心的原生配置项主要是:Info.plist 中的蓝牙权限描述(CoreBluetooth 使用说明)与 Capabilities 中的 Bluetooth 开关(推断,属于 Xcode 工程级配置,未在本仓库文件布局中直接体现)。
API Reference
注意:本页生成时源读取预算已耗尽,未能逐行核对各类的完整方法签名。以下仅列出已确认存在的类/文件及其职责边界;具体方法签名请以仓库源码为准。
BlePlugin.swift(插件入口)
- 职责:注册 Flutter 插件、绑定 MethodChannel/EventChannel、接收 Dart 方法调用并分发至各管理器、将原生事件与日志回传 Dart。
- 位置:BlePlugin.swift
JLBleManager(BLE 核心管理器)
- 职责:CoreBluetooth 扫描、连接、断开、外设回调汇聚,是直连路径的中枢;对外暴露扫描/连接/数据下发接口,内部协调
JLBleHandler与SingleDataSender。 - 位置:JLBleManager.h、JLBleManager.m
JLBleEntity(设备实体)
- 职责:描述一个 BLE 设备(标识、状态、广播数据、连接句柄),作为
JLBleManager与上层之间传递的设备模型。 - 位置:JLBleEntity.h
SingleDataSender(分包发送器)
- 职责:将大块数据按 MTU 拆分、串行写入特征值,等待每次写入回调后继续下一包,内置发送进度与结果回调。
- 位置:SingleDataSender.h
JLBleHandler 与 HandleBroadcastPtl(数据处理与广播协议)
- 职责:解析设备应答与广播包,将原始字节转换为结构化信息供上层使用。
- 位置:JLBleHandler.h、HandleBroadcastPtl.h
JLBleAssistManager(助手中转)与 DeviceManager(广播音箱)
- 职责:两条独立设备形态路径——经助手设备转发数据、以及广播音箱设备的连接/管理。
- 位置:JLBleAssistManager.h、DeviceManager.h
Failure Modes、边界情况与并发
以下分析基于目录结构与 BLE OTA 工程的通用约束(推断项已标注):
- 蓝牙未授权/未开启:CoreBluetooth 授权弹窗被拒或系统蓝牙关闭时,扫描/连接必然失败。原生层应在
JLBleManager的 central manager 状态回调处识别CBCentralManagerState异常并通过 EventChannel 上报(推断),提示用户在系统设置中开启权限。 - 连接中断与断点续传:OTA 过程中设备远离或断电会导致链路断开。分包发送(
SingleDataSender)若中断,需支持重连后的状态恢复或重新开始(推断:由上层决定续传策略,原生层需保证发送状态可查询)。 - MTU 与缓冲区溢出:若一次写入超过设备 MTU 或在上一个写入完成前就写入下一包,会造成丢包/卡死。
SingleDataSender采用"写回调后再发下一包"的串行模式正是为了规避该问题。 - 数据校验失败:链路层干扰可能导致包损坏。DFUnits 的 CRC16 与 AES 能力用于校验/解密,校验失败时应触发重发或终止流程(推断)。
- 并发/竞态:BLE 回调(centralManager 代理)与业务线程之间、以及多设备并发连接时,共享状态(如当前发送队列、连接句柄)需要串行保护;
JLBleManager作为单一中枢降低了竞态面,但发送队列的线程安全依赖其内部实现(未验证)。
Performance 与运维注意事项
- 分包粒度与流控:OTA 传输吞吐直接取决于 MTU 协商与
SingleDataSender的写入节奏。支持更大 MTU(如通过maximumWriteValueLength协商,推断)可显著减少包数量、降低总耗时;串行写入保证了吞吐与可靠性的平衡。 - 预编译 framework 集成:
DFUnits.framework以二进制形式随工程分发,接入方无需编译其源码,但升级 SDK 时需同步替换 framework 并核对头文件兼容性。该目录位于示例工程code/JL_OTA/example/ios/下,插件仓库与示例工程共用同一套依赖布局。 - 日志可观测性:
LogConstants.swift提供统一日志标签,联调时可按标签过滤原生侧日志,快速定位扫描、连接、发送各环节问题(推断:日志经 EventChannel 上报 Dart 层展示)。 - 状态上报频率:升级进度事件若逐包上报会刷爆通道,合理做法是聚合进度(如按百分比或按块)后经 EventChannel 上报(推断),降低 Dart 侧渲染与通道开销。
Extension Points
- 新增设备类型/芯片:在
Constant/DeviceTypeConstants.h增加类型常量,并在对应业务路径(直连/助手/音箱)中扩展流程分支。这是最常规的扩展入口。 - 新增方法通道命令:在
MethodChannelConstants.swift增加方法名,同时在BlePlugin.swift的分发逻辑中注册对应处理分支,并保持 Dart 侧同步。 - 新增协议解析:在
BleManager目录下仿照JLBleHandler/HandleBroadcastPtl增加协议处理类,由JLBleManager在收到原始数据时按协议类型分发。 - 新增设备形态:仿照
BleByAssist与BroadcastSpeakers目录结构,新增独立子目录承载专属管理类,避免把品类逻辑塞进通用JLBleManager——这正是现有目录划分所鼓励的扩展方式。
Tests
本次探索未在 code/JL_OTA/ios/ 与 code/JL_OTA/example/ios/ 中发现独立的 iOS 原生单元测试工程(未找到 *Tests 目标或 XCTest 源文件)。BLE 设备相关逻辑强依赖真实硬件,实践中多以真机 + 实体设备联调为主(推断)。若后续仓库补充 XCTest/UI 测试,建议优先覆盖 SingleDataSender 的分包边界与 HandleBroadcastPtl 的广播解析等纯逻辑部分。
Related Links
- Android 原生层架构:Android 侧对应实现,通道契约与 iOS 侧保持一致
- BlePlugin.swift:iOS 插件入口源文件
- JLBleManager.h:BLE 核心管理器
- MethodChannelConstants.swift:方法通道常量契约
- DFUnits.framework Headers:依赖工具库头文件目录