JLDialUnit 彩屏仓与表盘控制
JLDialUnit 是杰理(Jieli)iOS 蓝牙 SDK 中的表盘管理框架,负责彩屏设备(如智能手表)的文件资源仓(彩屏仓)管理与表盘(Dial)的读取、设置、自定义背景激活等控制操作。本页完整介绍该框架的公共 API、数据模型、核心流程与集成方式。
Purpose and Scope
本页面向需要集成 JLDialUnit 框架的 iOS 开发者,覆盖以下内容:
JLDialUnitMgr核心管理器:初始化、文件列表(彩屏仓)管理、文件传输/删除、剩余空间查询、表盘控制等全部公共接口;JLDialSourceModel数据模型:表盘/背景资源在 App 侧的表示形式;- 框架依赖的底层模块(
JL_BLEKit、JLFlashOpMgr、JLModel_File)及其协作关系; - 表盘更新、背景激活的端到端控制流、失败模式与注意事项。
不涉及的内容(由同级目录页负责):JL_BLEKit 底层蓝牙连接与协议细节(见 3.1 相关章节)、JLDialUnit 之外的音频/文件系统框架(如 JL_FlashOperate 的通用文件操作页)。本页聚焦于 彩屏仓(文件资源管理)与表盘控制 这一能力边界。
Overview
JLDialUnit 以 xcframework 二进制框架形式发布(包含 ios-arm64 与 ios-arm64_x86_64-simulator 双架构),公开头文件由伞头 JLDialUnit.h 统一导出:
#import <JLDialUnit/DialManager.h>
#import <JLDialUnit/FatfsObject.h>
#import <JLDialUnit/ZipZap.h>
#import <JLDialUnit/ZZArchive.h>
#import <JLDialUnit/ZZArchiveEntry.h>
#import <JLDialUnit/ZZConstants.h>
#import <JLDialUnit/ZZError.h>
#import <JLDialUnit/JLDialSourceModel.h>
#import <JLDialUnit/JLDialUnitMgr.h>
#import <JLDialUnit/JLSourceUpdate.h>
Source: JLDialUnit.h
从导出头文件可以归纳框架的职责分层:
JLDialUnitMgr(入口门面):面向业务层的唯一主要入口,封装了彩屏仓文件操作与表盘控制;JLDialSourceModel(资源模型):继承自JL_BLEKit的JLModel_File,附加size、crc字段,表示一个可下发/可管理的表盘或背景文件;DialManager/FatfsObject/JLSourceUpdate:内部文件系统(FATFS)操作与资源更新辅助类;ZipZap/ZZArchive系列:ZIP 归档解析工具,用于解包表盘资源包(表盘通常以压缩包形式分发)。
框架底层依赖 JL_BLEKit(蓝牙协议栈,提供 JL_ManagerM 连接管理、JLFlashOpMgr Flash 操控句柄、JLModel_File 设备文件模型、JL_CardType/JL_FileHandleType 枚举),以及 JLLogHelper(日志)与 JL_HashPair(哈希配对)。
设计意图:框架层把"设备文件系统"抽象为"App 侧资源仓"。开发者不需要理解 BLE 协议如何分片传输、FATFS 如何寻址,只需操作 JLDialSourceModel 对象数组,由 JLDialUnitMgr 内部通过 JLFlashOpMgr 完成与设备的同步。
Architecture
flowchart TD
subgraph sg_App["App 应用层"]
App["App 业务代码"]
end
subgraph sg_DialUnit["JLDialUnit 框架"]
Mgr["JLDialUnitMgr"]
SourceModel["JLDialSourceModel"]
DialMgr["DialManager"]
Fatfs["FatfsObject"]
Zip["ZipZap / ZZArchive"]
Update["JLSourceUpdate"]
end
subgraph sg_BLEKit["JL_BLEKit 底层"]
BLEKit["JL_ManagerM"]
FlashOp["JLFlashOpMgr"]
FileModel["JLModel_File"]
CardType["JL_CardType"]
end
App -->|"初始化/调用"| Mgr
Mgr -->|"持有"| SourceModel
Mgr -->|"文件列表"| CardType
Mgr -->|"Flash 操控"| FlashOp
Mgr -->|"内部辅助"| DialMgr
Mgr -->|"内部辅助"| Fatfs
Mgr -->|"内部辅助"| Zip
Mgr -->|"内部辅助"| Update
SourceModel -->|"继承"| FileModel
FlashOp -->|"BLE 通道"| BLEKit
架构说明:
JLDialUnitMgr是框架对外暴露的"门面"(Facade),业务层只需持有这一个对象即可完成彩屏仓与表盘的全部操作;JLDialSourceModel继承JLModel_File(来自JL_BLEKit),使设备侧文件模型可以在 App 侧直接复用,同时新增size(文件大小)与crc(校验值)以支撑表盘资源包完整性校验;JLFlashOpMgr是JL_BLEKit提供的 Flash 操控句柄,JLDialUnitMgr通过属性currentFlaghOpMgr暴露给上层,实际的文件读取、写入、删除指令都由它承载;DialManager、FatfsObject、ZipZap、JLSourceUpdate为框架内部实现细节,外部一般不直接使用,但可通过伞头引用;- 每次
JLDialUnitMgr初始化都绑定一个JL_ManagerM(蓝牙管理对象),即 一个蓝牙连接对应一个表盘管理器实例。
JLDialUnitMgr 核心管理器详解
JLDialUnitMgr 是框架的核心类,接口定义于 JLDialUnitMgr.h。其 API 按职责可分为三组:状态属性、彩屏仓(文件)操作、表盘控制。
状态属性
@property (nonatomic,strong)NSMutableArray <JLDialSourceModel *> *fileArray;
@property (nonatomic, strong) JLFlashOpMgr *currentFlaghOpMgr;
@property (nonatomic,strong)JLDialSourceModel *currentFileSource;
@property (nonatomic,strong)JLDialSourceModel *_Nullable currentBackground;
Source: JLDialUnitMgr.h
fileArray:App 侧缓存的设备文件列表(即"彩屏仓"),由getFileList:/updateFileSources:填充;currentFlaghOpMgr:当前 Flash 操控句柄,是框架与设备文件系统交互的桥梁;currentFileSource:当前正在使用的表盘文件模型(设置表盘后更新);currentBackground:当前使用的自定义表盘背景(可空,未激活自定义背景时为nil)。
初始化与生命周期
-(instancetype)initWithManager:(JL_ManagerM*)mgr completion:(void (^)(NSError * _Nullable))completion;
Source: JLDialUnitMgr.h
初始化需要传入一个已建立(或可建立)连接的 JL_ManagerM。完成回调返回 NSError,为 nil 表示初始化成功。设计上采用 回调而非同步返回,是因为初始化内部可能涉及与设备的握手/能力探测,属于异步操作。框架以强引用持有 JLFlashOpMgr(currentFlaghOpMgr),因此 JLDialUnitMgr 的生命周期应长于一次表盘操作会话,通常与蓝牙连接会话保持一致。
彩屏仓:文件列表管理
-(void)getFileList:(JL_CardType)cardType count:(NSInteger)count completion:(void (^)(NSArray <JLDialSourceModel *> * _Nullable,NSError * _Nullable))completion;
-(void)cleanFileList;
-(void)updateFileSources:(NSArray<JLModel_File *> *)fileSources completion:(void (^)(NSArray<JLDialSourceModel *> * _Nullable,NSError * _Nullable))completion;
Source: JLDialUnitMgr.h
getFileList:count:completion::按存储卡类型(JL_CardType,如内置 Flash 或外置 TF 卡)请求文件列表。count为请求的条目数,回调返回的是累计数目——即支持分页累加获取,业务层可多次调用直至取完全部文件;cleanFileList:清空fileArray,用于切换设备/刷新前重置本地缓存;updateFileSources:completion::直接用JL_BLEKit层拿到的JLModel_File数组更新列表。框架内部会将JLModel_File逐个转换为JLDialSourceModel(见下文模型转换),转换失败时通过error回调上报。
设计意图:getFileList 走框架内部分页累加逻辑,updateFileSources 则允许业务层在已经通过其他途径(如 JL_BLEKit 原生接口)获得文件模型时直接注入,避免重复读取,保持与底层数据源的一致性。
彩屏仓:文件写入、删除与空间查询
-(void)updateFileToDevice:(JL_FileHandleType)fileHandleType Data:(NSData *)fileData FilePath:(NSString *)filePath completion:(void (^)(int,double,NSError * _Nullable))resultCompletion;
-(void)deleteFile:(NSString *)filePath getFileList:(BOOL)isGet Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion;
-(void)deleteFiles:(NSArray <NSString*>*) filePathList Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion;
-(void)getRemainingSpace:(void(^)(int freeSize, NSError * _Nullable error))completion;
-(void)getFlashRemainingSpace:(void(^)(int freeSize, NSError * _Nullable error))completion;
Source: JLDialUnitMgr.h
updateFileToDevice:Data:FilePath:completion::将表盘/背景资源数据(NSData)下发到设备。fileHandleType指定目标存储句柄(JL_FileHandleType),filePath指定设备侧存储路径。回调三元组含义:state(0 成功 / 1 传输中 / -1 失败)、progress(传输进度 0~100)、error。进度回调是幂等可重复触发的,业务层应据此刷新进度条 UI;deleteFile:getFileList:Completion:/deleteFiles:Completion::单个/批量删除设备文件。单个删除可通过isGet控制删除完成后是否自动重新拉取文件列表(保持fileArray同步);getRemainingSpace:与getFlashRemainingSpace::查询剩余空间(单位由设备协议决定,通常为字节/KB),前者为通用接口,后者为 V1 版本专用接口。下发大文件前应先查询剩余空间,避免写入失败。
表盘控制
-(void)dialGetCurrent:(void (^)(JLDialSourceModel * _Nullable))completion;
-(void)dialSetCurrent:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion;
-(void)dialActiveCustomBackground:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion;
-(void)dialResetCustomBackground:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion;
-(void)dialGetDial:(NSString *)path BackgroundInfo:(void (^)(JLDialSourceModel * _Nullable model, NSError * _Nullable error))resultCompletion;
Source: JLDialUnitMgr.h
dialGetCurrent::查询设备当前正在使用的表盘,结果模型经currentFileSource同步;dialSetCurrent:Completion::切换设备当前表盘。state == YES表示成功;失败时error携带原因(如文件不存在、CRC 校验失败);dialActiveCustomBackground:Completion::激活自定义表盘背景。背景文件须已存在于设备(通常先经updateFileToDevice:下发),激活后currentBackground更新;dialResetCustomBackground::重置自定义背景,恢复默认背景;dialGetDial:BackgroundInfo::按路径(如/WATCH)查询目录下的表盘背景信息,返回该背景的JLDialSourceModel。
设计意图:表盘与背景分离为两套指令,是因为彩屏设备通常支持"表盘主体 + 可替换背景"的组合模式。dialSetCurrent: 面向表盘主体切换,dialActiveCustomBackground: 面向背景层替换,二者独立状态,框架通过 currentFileSource 与 currentBackground 分别跟踪。
JLDialSourceModel 数据模型
@interface JLDialSourceModel : JLModel_File
@property (nonatomic, assign)int size;
@property (nonatomic, assign)uint16_t crc;
+(JLDialSourceModel *)createModel:(JLModel_File *)model;
@end
Source: JLDialSourceModel.h
JLDialSourceModel 是 JLModel_File(设备文件模型,含文件名、路径、属性等)的子类,扩展了两个字段:
size:资源文件字节大小,用于传输前校验与进度计算;crc:16 位 CRC 校验值,用于表盘资源完整性校验(设备侧写入后回读比对,或作为设置表盘前的合法性依据)。
工厂方法 createModel: 负责从底层 JLModel_File 转换,转换时拷贝文件元信息并(可能)读取设备侧返回的 size/crc。该工厂模式将"底层模型 → 业务模型"的映射集中在一处,避免业务层散落转换逻辑。
核心流程:表盘资源下发与切换
一次完整的"表盘更新"端到端流程如下(以时序图展示 App 与框架、底层、设备间的协作):
sequenceDiagram
participant App as App 业务层
participant Mgr as JLDialUnitMgr
participant Flash as JLFlashOpMgr
participant Dev as 设备 (BLE)
participant Model as JLDialSourceModel
App->>Mgr: initWithManager:completion:
Mgr-->>App: completion(nil) 初始化完成
App->>Mgr: getRemainingSpace:
Mgr->>Flash: 查询剩余空间
Flash-->>Mgr: freeSize
Mgr-->>App: freeSize / error
App->>Mgr: updateFileToDevice:Data:FilePath:
Mgr->>Flash: 分片写入文件
Flash-->>Mgr: progress 0~100
Mgr-->>App: state=1, progress
Flash-->>Mgr: 写入完成
Mgr-->>App: state=0, progress=100
App->>Mgr: getFileList:count:
Mgr->>Flash: 拉取设备文件
Flash-->>Mgr: JLModel_File 列表
Mgr->>Model: createModel: 转换
Mgr-->>App: JLDialSourceModel 数组
App->>Mgr: dialSetCurrent:Completion:
Mgr->>Flash: 下发设置表盘指令
Flash-->>Dev: BLE 指令
Dev-->>Flash: 应答(CRC 校验)
Flash-->>Mgr: 结果
Mgr-->>App: state=YES / error
流程要点:
- 初始化:
initWithManager:绑定蓝牙连接,异步回调确认就绪; - 空间预检(推荐):大文件下发前先查询剩余空间,避免中途失败;
- 文件下发:
updateFileToDevice:内部由JLFlashOpMgr完成 BLE 分片传输,state=1+progress回调驱动进度条; - 列表刷新:下发完成后通过
getFileList:(或deleteFile:getFileList:YES)刷新fileArray,使新文件出现在彩屏仓中; - 表盘切换:
dialSetCurrent:从彩屏仓中选中目标模型,设备侧校验(含 CRC)后切换生效。
设置自定义背景的补充流程
flowchart LR
A["dialGetDial:/WATCH 查询背景"] --> B["updateFileToDevice: 下发背景资源"]
B --> C["getFileList: 确认文件已存在"]
C --> D{"dialActiveCustomBackground:"}
D -->|"state=YES"| E["currentBackground 更新"]
D -->|"state=NO"| F["error 上报,保留原背景"]
自定义背景流程比表盘切换多一步"激活":背景文件先作为普通文件下发到设备(如 /WATCH 目录),随后调用 dialActiveCustomBackground: 使设备将其作为当前背景渲染。若需恢复默认,调用 dialResetCustomBackground:。
使用示例
以下示例均依据 JLDialUnitMgr.h 的公开接口编写,展示了业务层的典型调用方式。
初始化管理器
// 假设已通过 JL_BLEKit 获得蓝牙管理对象 mgr
JLDialUnitMgr *dialMgr = [[JLDialUnitMgr alloc] initWithManager:mgr completion:^(NSError * _Nullable error) {
if (error) {
// 初始化失败:检查蓝牙连接状态后重试
} else {
// 初始化成功,可开始拉取彩屏仓
}
}];
Source: JLDialUnitMgr.h
拉取彩屏仓文件列表(分页累加)
[dialMgr getFileList:JL_CardTypeFlash count:20 completion:^(NSArray<JLDialSourceModel *> * _Nullable models, NSError * _Nullable error) {
if (error == nil && models.count > 0) {
// 回调返回的是累计数目,可继续调用直至取完
// models 已同步进 dialMgr.fileArray
}
}];
Source: JLDialUnitMgr.h
下发表盘资源到设备并设置当前表盘
// 1. 下发资源文件
[dialMgr updateFileToDevice:JL_FileHandleTypeFlash Data:dialData FilePath:@"/WATCH/dial_01.bin"
completion:^(int state, double progress, NSError * _Nullable error) {
if (state == 1) {
// 传输中:progress 0~100,刷新进度条
} else if (state == 0) {
// 传输成功:刷新列表并设置表盘
[dialMgr getFileList:JL_CardTypeFlash count:50 completion:^(NSArray<JLDialSourceModel *> * _Nullable models, NSError * _Nullable err) {
JLDialSourceModel *target = [dialMgr.fileArray filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:@"path == %@", @"/WATCH/dial_01.bin"]].firstObject;
if (target) {
[dialMgr dialSetCurrent:target Completion:^(BOOL state, NSError * _Nullable error) {
// state == YES:表盘已切换
}];
}
}];
} else {
// state == -1:失败,error 携带原因
}
}];
Source: JLDialUnitMgr.h 与 JLDialUnitMgr.h
激活 / 重置自定义背景
// 激活自定义背景(文件已存在于设备 /WATCH 目录)
[dialMgr dialActiveCustomBackground:backgroundModel Completion:^(BOOL state, NSError * _Nullable error) {
// state == YES:背景已生效,dialMgr.currentBackground 已更新
}];
// 恢复默认背景
[dialMgr dialResetCustomBackground:^(BOOL state, NSError * _Nullable error) {
// state == YES:已重置
}];
Source: JLDialUnitMgr.h
注意:以上示例中的
JL_CardTypeFlash、JL_FileHandleTypeFlash为JL_BLEKit枚举的示意值,实际枚举名以JL_BLEKit头文件为准;JLDialSourceModel的属性访问方式(如path)继承自JLModel_File,字段名以JL_BLEKit定义为准。
API 参考
JLDialUnitMgr 完整方法签名
初始化
- (instancetype)initWithManager:(JL_ManagerM *)mgr completion:(void (^)(NSError * _Nullable))completion
创建表盘管理器实例。
- 参数:
mgr(JL_ManagerM *):蓝牙管理对象,须为已初始化且可用的连接;completion(block):初始化结果回调,error == nil表示成功。
- 返回:
JLDialUnitMgr实例。 - 设计说明:异步初始化,内部可能执行设备能力握手。
彩屏仓(文件列表)
- (void)getFileList:(JL_CardType)cardType count:(NSInteger)count completion:(void (^)(NSArray<JLDialSourceModel *> * _Nullable, NSError * _Nullable))completion
获取设备存储中的文件列表(支持分页累加)。
- 参数:
cardType(JL_CardType):设备存储类型(内置 Flash / 外置卡);count(NSInteger):本次请求的条目数;completion(block):返回累计的模型数组;error非空表示失败。
- 注意:多次调用返回的数组是累计结果,业务层应循环调用直到取完或返回错误。
- (void)cleanFileList
清空本地 fileArray 缓存。无参数、无回调。适用于切换设备或强制刷新前。
- (void)updateFileSources:(NSArray<JLModel_File *> *)fileSources completion:(void (^)(NSArray<JLDialSourceModel *> * _Nullable, NSError * _Nullable))completion
以底层 JLModel_File 数组直接更新列表,内部转换为 JLDialSourceModel。
- 参数:
fileSources:来自JL_BLEKit层的设备文件模型数组;completion:转换后的模型数组;转换失败时error非空。
彩屏仓(文件传输)
- (void)updateFileToDevice:(JL_FileHandleType)fileHandleType Data:(NSData *)fileData FilePath:(NSString *)filePath completion:(void (^)(int, double, NSError * _Nullable))resultCompletion
将文件数据下发到设备指定路径。
- 参数:
fileHandleType(JL_FileHandleType):目标存储句柄类型;fileData(NSData *):文件二进制数据;filePath(NSString *):设备侧存储路径(如/WATCH/xxx.bin);resultCompletion(block):参数依次为state(0 成功 / 1 传输中 / -1 失败)、progress(0~100)、error。
- 注意:
state == 1时progress持续更新,勿在此时移除回调对象。
- (void)deleteFile:(NSString *)filePath getFileList:(BOOL)isGet Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion
删除单个文件。
- 参数:
filePath:设备侧文件路径;isGet:删除完成后是否自动重新拉取文件列表;resultCompletion:state == YES表示删除成功。
- (void)deleteFiles:(NSArray<NSString *> *)filePathList Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion
批量删除多个文件(filePathList 为路径数组)。
- (void)getRemainingSpace:(void (^)(int freeSize, NSError * _Nullable error))completion
查询设备剩余空间(通用接口)。freeSize 为剩余字节数(单位以设备协议为准)。
- (void)getFlashRemainingSpace:(void (^)(int freeSize, NSError * _Nullable error))completion
查询 Flash 剩余空间(V1 版本专用接口)。
表盘控制
- (void)dialGetCurrent:(void (^)(JLDialSourceModel * _Nullable))completion
获取设备当前使用的表盘模型;设备无表盘时回调 nil。
- (void)dialSetCurrent:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion
设置当前表盘。
- 参数:
model:目标表盘模型(须来自fileArray或dialGetCurrent:结果);resultCompletion:state == YES成功;失败时error说明原因(如文件不存在、校验失败)。
- 副作用:成功后
currentFileSource更新为model。
- (void)dialActiveCustomBackground:(JLDialSourceModel *)model Completion:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion
激活自定义表盘背景(文件须已存在于设备)。
- 副作用:成功后
currentBackground更新为model。
- (void)dialResetCustomBackground:(void (^)(BOOL state, NSError * _Nullable error))resultCompletion
重置自定义表盘背景为默认背景。
- 副作用:成功后
currentBackground置为nil。
- (void)dialGetDial:(NSString *)path BackgroundInfo:(void (^)(JLDialSourceModel * _Nullable model, NSError * _Nullable error))resultCompletion
按路径查询表盘背景信息。
- 参数:
path:目录路径,如/WATCH;resultCompletion:返回该目录下背景资源的JLDialSourceModel;查询失败时error非空。
JLDialSourceModel 方法
+ (JLDialSourceModel *)createModel:(JLModel_File *)model
从底层 JLModel_File 创建 JLDialSourceModel(拷贝文件元信息并填充 size/crc)。
- 参数:
model(JLModel_File *):设备文件模型。 - 返回:
JLDialSourceModel *。
关联底层类型(来自 JL_BLEKit)
| 类型 | 说明 |
|---|---|
JL_ManagerM | 蓝牙协议栈管理对象,JLDialUnitMgr 初始化的入参 |
JLFlashOpMgr | Flash 操控句柄,currentFlaghOpMgr 属性类型,承载文件读写删除指令 |
JLModel_File | 设备文件模型,JLDialSourceModel 的父类 |
JL_CardType | 存储卡类型枚举(文件列表查询参数) |
JL_FileHandleType | 文件存储句柄类型枚举(文件下发参数) |
配置选项
JLDialUnit 作为二进制框架不提供运行时配置文件(无 plist/appsettings 类配置)。"配置"主要体现在以下调用参数与状态上:
| 配置/参数 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
cardType(getFileList:) | JL_CardType | 由设备决定 | 文件列表查询的存储介质(Flash / 外置卡) |
count(getFileList:) | NSInteger | 业务自定 | 单次请求条目数,回调返回累计值 |
fileHandleType(updateFileToDevice:) | JL_FileHandleType | 由目标存储决定 | 下发目标存储句柄 |
filePath | NSString | 如 /WATCH | 设备侧目录/文件路径,决定资源归类 |
isGet(deleteFile:) | BOOL | 业务自定 | 删除后是否自动刷新列表 |
提示:
JL_CardType、JL_FileHandleType、JLModel_File字段名等具体枚举/属性定义位于JL_BLEKit框架头文件中,集成时请查阅对应头文件以获取准确取值。
失败模式、边界情况与并发
以下结论基于公共头文件契约与框架设计推断,具体错误码以设备协议/JL_BLEKit 文档为准。
常见失败模式
| 场景 | 表现 | 处理建议 |
|---|---|---|
| 蓝牙未连接/连接断开 | initWithManager: 回调 error;后续所有操作回调 error | 检查连接状态,重连后重建或复用管理器 |
| 剩余空间不足 | updateFileToDevice: 回调 state == -1 | 下发前调用 getRemainingSpace: 预检,提示用户清理空间 |
| 目标路径不存在/非法 | 写入或设置表盘失败 | 先 getFileList: 确认目录结构,使用约定路径(如 /WATCH) |
| 文件 CRC 校验失败 | dialSetCurrent: 返回失败 | 重新下发资源(size/crc 字段可用于本地校验比对) |
| 分页拉取中断 | getFileList: 累计数组不完整 | 以回调 error 为终止条件,失败后重试或 cleanFileList 后重建 |
| 表盘/背景对象已过期 | 设置时设备侧找不到对应文件 | 设置前从最新 fileArray 中重新取模型,勿缓存旧对象 |
边界情况
- 空彩屏仓:
dialGetCurrent:回调nil,业务层应处理"设备无表盘"的引导界面; - 自定义背景未激活:
currentBackground为nil,调用dialResetCustomBackground:幂等返回成功即可; - 大批量删除:
deleteFiles:一次性传入较多路径时,建议分批调用并依赖回调确认,避免单次指令超时; - 分页参数:
getFileList:count:的count过大可能超出设备单次响应能力,建议小步多次(如每次 20~50)。
并发与一致性
- 回调线程:框架回调(
completion/resultCompletion)的线程未在头文件中约定,建议业务层在回调内切换到主线程再刷新 UI(dispatch_async(dispatch_get_main_queue(), ...)); - 串行操作:同一
JLDialUnitMgr上的文件写入/删除/表盘设置应避免并发发起——BLE 通道本质上是串行分时复用,并发指令可能导致传输交错。推荐引入串行队列(如NSOperationQueue或信号量)包装所有框架调用; - 状态一致性:
fileArray由框架内部维护,业务层若通过updateFileSources:注入数据,应与getFileList:的结果语义保持一致;删除文件时优先使用deleteFile:getFileList:YES让框架自动同步列表; - 实例生命周期:一个
JLDialUnitMgr对应一个JL_ManagerM。设备切换时必须创建新实例并cleanFileList,不能复用旧实例(其内部currentFlaghOpMgr指向旧连接)。
性能与运维注意事项
- 传输进度:
updateFileToDevice:通过state == 1+progress高频回调驱动 UI,注意避免在回调中做重活(如图片解码、磁盘 IO),防止进度卡顿; - 大文件策略:表盘资源包(尤其含 ZIP 解包流程的场景)体积较大,建议:① 下发前压缩(框架内
ZipZap/ZZArchive提供解包能力,压缩可在服务端完成);② 拆分下发批次;③ 结合size/crc做完整性校验后重试; - 日志:框架依赖
JLLogHelper输出日志,排查问题时开启该日志模块可跟踪文件操作指令序列; - 升级兼容:
getFlashRemainingSpace:为 V1 专用接口,新设备建议使用通用getRemainingSpace:,并通过能力判断选择调用。
扩展点
- 资源模型扩展:
JLDialSourceModel继承自JLModel_File,业务层可继续子类化,附加自定义元数据(如缩略图、描述文案),在getFileList:回调中包装后使用; - 列表注入:
updateFileSources:允许业务层从其他数据源(服务端目录、历史缓存)注入文件模型,实现"离线彩屏仓"或混合数据源场景; - 背景组合:
dialActiveCustomBackground:与dialResetCustomBackground:提供背景层开关,可结合多套背景素材实现"表盘换肤"产品功能; - 文件打包:
ZipZap/ZZArchive/ZZArchiveEntry头文件对外公开,业务层可直接用于解析或生成表盘 ZIP 资源包(如从服务端下载的压缩表盘包解包后经updateFileToDevice:下发)。
测试与验证建议
仓库内未包含 JLDialUnit 框架的单元测试源码(框架以编译后 xcframework 形式发布,见 libs/JLDialUnit.xcframework)。集成验证可从以下角度覆盖:
- 链路冒烟:初始化 →
getRemainingSpace:→getFileList:→dialGetCurrent:,确认设备在线且协议可用; - 下发回环:
updateFileToDevice:到 100% 后getFileList:确认文件出现,再deleteFile:删除并确认列表同步; - 表盘切换:多表盘交替
dialSetCurrent:,校验设备侧实际显示与dialGetCurrent:返回值一致; - 背景激活:激活 → 重置 → 再激活,验证
currentBackground状态机与设备显示一致; - 异常注入:断连、空间不足、非法路径、重复调用场景下,验证回调
error与业务提示的健壮性。
相关链接
- JLDialUnitMgr.h(核心管理器接口)
- JLDialUnit.h(框架伞头)
- JLDialSourceModel.h(资源数据模型)
- 集成 Demo:code/JieLi_Home_Demo(含 JLDialUnit 框架引用示例)
- 蓝牙协议栈
JL_BLEKit:提供JL_ManagerM、JLFlashOpMgr、JLModel_File、JL_CardType等底层类型,详见 SDK 框架目录对应页面 - 日志与工具库:
JLLogHelper(日志)、JL_HashPair(哈希配对),均为 JLDialUnit 的编译依赖