连接方式选择指南
本文档说明 iOS-JL_OTA SDK 提供的三种蓝牙连接方式(原生 CoreBluetooth、JL_BLEKit、JL_Assist 自定义连接)的适用场景、架构差异与选择标准,帮助开发者在集成 OTA 升级之前确定正确的连接方案。
Purpose and Scope
本页是「开始使用」系列中的连接方式选型指南,覆盖以下内容:
- 三种连接方式的定位与职责边界(谁负责扫描、连接、服务发现、分包发送)
- 各方式对应的官方示例工程与集成路径
- 选择决策流程与三种方式的对比总览
- 与连接方式相关的核心调用流程、配置项、失败模式与重连策略
以下主题属于相邻专题页面,本页只做指引、不展开:
- OTA 升级接口的完整 API 规范(
getOTAManager、cmdOTAData、noteEntityConnected等)→ 见仓库根目录doc/API 说明.md - 基于某一种连接方式的完整升级开发示例 → 见各 Demo 目录下的「OTA 升级开发示例」文档(
code/MiniDemo/MiniSingleDemo/、code/MiniDemo/JLBleKitOTADemo/、code/MiniDemo/JLAssistOTADemo/)
Overview
iOS-JL_OTA 是杰理科技为杰理蓝牙设备提供的 OTA 升级开发平台,基于 RCSP 协议(远程控制系统协议),支持数传设备、手表、音箱等产品线。SDK 将「OTA 升级逻辑」与「蓝牙连接细节」解耦:升级逻辑统一由 JL_OTAManager 提供,而底层数据通道可以来自三种不同的蓝牙连接实现。
README 的「连接方式选择」一节明确列出了三种方式及其适用场景(见 README.md#L85-L98):
| 连接方式 | 适用场景 | Demo 路径 |
|---|---|---|
| 原生 CoreBluetooth | 完全掌控 BLE 扫描、连接、服务与分包发送 | code/MiniDemo/MiniSingleDemo/ |
| JL_BLEKit | 快速集成、减少蓝牙细节处理 | code/MiniDemo/JLBleKitOTADemo/ |
| JL_Assist 自定义 | 已有外部蓝牙管控或需桥接到既有蓝牙层 | code/MiniDemo/JLAssistOTADemo/ |
README 给出的选择指南(原文摘录):
- 完全掌控 BLE 扫描、连接、服务与分包发送 → 选择原生自定义连接
- 快速集成、减少蓝牙细节处理 → 选择 SDK 蓝牙连接(JL_BLEKit)
- 已有外部蓝牙管控或需桥接到既有蓝牙层 → 选择 JL_Assist 自定义连接
设计意图:SDK 不强制绑定某一种蓝牙栈。三种方式共享同一套 OTA 能力层(JL_OTALib、JL_AdvParse 广播解析、JL_HashPair 配对认证),只是「数据通道接入点」不同。这样既满足需要深度定制 BLE 行为的开发者,也满足希望快速上线的开发者,且三种方式的升级协议栈(RCSP)完全一致,升级结果行为一致。
Architecture
flowchart TD
subgraph sg_App["应用层(你的 iOS App)"]
App["业务代码 / 升级界面"]
end
subgraph sg_Conn["连接方式层(三选一)"]
CB["原生 CoreBluetooth<br/>自建 BleManager"]
BK["JL_BLEKit<br/>SDK 蓝牙连接"]
AS["JL_Assist<br/>自定义蓝牙连接"]
end
subgraph sg_OTA["OTA 能力层(共享)"]
OM["JL_OTAManager"]
Parser["JL_AdvParse 广播解析"]
Hash["JL_HashPair 配对认证"]
end
subgraph sg_Dev["设备端"]
Dev["杰理蓝牙设备<br/>(RCSP 协议固件)"]
end
App --> CB
App --> BK
App --> AS
CB --> OM
BK --> OM
AS --> OM
OM --> Parser
OM --> Hash
CB -->|"BLE GATT 通道"| Dev
BK -->|"BLE GATT 通道"| Dev
AS -->|"BLE GATT 通道(桥接)"| Dev
架构说明:应用层只面向 JL_OTAManager 编程,不关心底层数据通道来自哪种连接实现。连接方式层负责建立与设备的 GATT 数据通道:接收方向把设备数据回调给 OTA 层(通过 otaDataSend 委托),发送方向把 OTA 层产出的升级包写入设备。无论选择哪种连接方式,OTA 能力层与设备端固件交互的协议栈完全一致,因此升级结果具有一致性;差别仅在于「扫描/连接/订阅/写数据」这些 BLE 细节由谁负责。
三种连接方式详解
原生 CoreBluetooth(自建 BleManager)
定位:完全掌控 BLE 扫描、连接、服务与分包发送。适合已有自定义 BLE 管理逻辑、需要处理多设备/多服务、或需要精细控制连接参数的工程。
职责边界:开发者自行实现 CBCentralManager 扫描、CBPeripheral 连接、服务/特征发现、setNotifyValue 订阅、以及升级分包发送。SDK 侧的 JL_OTAManager 只负责 RCSP 协议打包与升级状态机。仓库中 code/JL_OTA/BleManager/ 即「自定义蓝牙连接实现」的参考工程目录(见 README.md#L109-L120)。
优点:对 BLE 细节的掌控力最强,方便与既有蓝牙业务(如设备配网、多连接)融合;不引入额外的蓝牙管理依赖。
代价:需要自行处理扫描过滤、连接超时、服务发现失败、分包/粘包、断线重连等全部细节,集成工作量最大。
JL_BLEKit(SDK 蓝牙连接)
定位:快速集成、减少蓝牙细节处理。适合以 OTA 为主要目标、不希望维护复杂 BLE 状态机的工程。
职责边界:扫描、连接、订阅、数据收发均由 SDK 蓝牙框架托管,开发者只需按 SDK 蓝牙连接的示例流程调用并接收连接状态回调,随后把设备句柄交给 JL_OTAManager 即可进入升级流程。对应示例工程为 code/MiniDemo/JLBleKitOTADemo/。
优点:集成路径最短,蓝牙细节由 SDK 封装,升级链路(连接 → 订阅 → 升级 → 回连)的一致性由 SDK 保证。
代价:对底层 BLE 行为的定制能力弱于原生方式;若工程已有独立蓝牙栈,则存在两套蓝牙管理并存的协调成本。
JL_Assist 自定义连接
定位:已有外部蓝牙管控或需桥接到既有蓝牙层。适合 App 中已经存在完整的蓝牙连接管理(例如自有蓝牙框架、跨端共享的蓝牙层),不希望为 OTA 再引入一套连接逻辑的工程。
职责边界:外部蓝牙层负责扫描与连接,JL_Assist 充当「桥接适配器」,把 OTA 层的数据写入请求(bleWrite)转发给既有蓝牙层,并把设备上报的数据回传给 JL_OTAManager。示例见 code/MiniDemo/JLAssistOTADemo/。
优点:与既有蓝牙架构解耦最小,改造范围仅限于桥接写入与回调转发两处;可复用 App 已有的扫描、重连、权限流程。
代价:需要开发者保证桥接层的写操作与订阅回调语义正确(数据分包、写入队列、断线通知),桥接质量直接影响升级稳定性。
对比总览
| 维度 | 原生 CoreBluetooth | JL_BLEKit | JL_Assist 自定义 |
|---|---|---|---|
| 扫描/连接职责 | 开发者自建 BleManager | SDK 托管 | 外部既有蓝牙层 |
| 服务/特征订阅 | 开发者实现 | SDK 封装 | 外部层实现,桥接回调 |
| 分包发送 | 开发者实现(写入队列) | SDK 封装 | 桥接 bleWrite |
| 集成工作量 | 最大 | 最小 | 中等(桥接适配) |
| BLE 定制能力 | 最强 | 较弱 | 取决于外部层 |
| 示例工程 | MiniSingleDemo/ | JLBleKitOTADemo/ | JLAssistOTADemo/ |
选择决策流程
flowchart TD
Start([开始选型]) --> Q1{"已有外部蓝牙管控<br/>或既有蓝牙层?"}
Q1 -->|"是"| AS["JL_Assist 自定义连接<br/>桥接既有蓝牙层<br/>(JLAssistOTADemo)"]
Q1 -->|"否"| Q2{"需要完全掌控 BLE 细节?<br/>扫描/连接/服务/分包"}
Q2 -->|"是"| CB["原生 CoreBluetooth<br/>自建 BleManager<br/>(MiniSingleDemo)"]
Q2 -->|"否"| BK["JL_BLEKit<br/>SDK 蓝牙连接<br/>(JLBleKitOTADemo)"]
AS --> Verify["按对应 Demo 集成并验证升级"]
CB --> Verify
BK --> Verify
核心调用流程
无论选择哪种连接方式,进入 OTA 升级后的核心调用序列是统一的:设备连接 + 订阅 → noteEntityConnected → cmdTargetFeature → cmdOTAData(data) → 委托回调 → 断开时 noteEntityDisconnected(见 README.md#L100-L105)。
sequenceDiagram
participant App as App 业务层
participant Conn as 连接方式层(三选一)
participant OM as JL_OTAManager
participant Dev as 杰理蓝牙设备
App->>Conn: 扫描并连接设备
Conn->>Dev: 连接 + 订阅特征
Dev-->>Conn: 已连接
Conn->>OM: 通知设备已连接(noteEntityConnected)
Note over OM: 填充 mBLE_UUID / mBLE_NAME 上下文
OM->>Dev: cmdTargetFeature(查询升级能力)
Dev-->>OM: 特性返回(单/双备份、强制升级等)
OM->>Dev: cmdOTAData(data)(分包发送升级包)
Dev-->>OM: 升级进度 / 结果
OM-->>App: 委托回调 otaUpgradeResult / otaDataSend
Note over OM,Dev: 升级完成,或断开 / 升级失败
App->>OM: noteEntityDisconnected(清理上下文)
关键点说明:
- 连接与订阅先于 OTA 上下文创建:只有底层通道就绪(特征订阅成功)后,才应调用
noteEntityConnected,否则数据会丢失。 mBLE_UUID/mBLE_NAME是 OTA 重连的依据:JL_OTAManager依靠这两个属性(以及bleAddr)在升级中断后发起回连(详见「失败模式与重连处理」)。cmdTargetFeature是升级前的能力协商:先查询设备支持单备份/双备份、是否强制升级等特性,SDK 据此选择升级策略。cmdOTAData分包由 OTA 层驱动:SDK 通过otaDataSend委托把每个数据包交给连接方式层写出,开发者只需保证写队列按序可靠送达。
使用示例
以下示例均取自官方 Demo 文档,展示「JL_Assist 自定义连接 + JL_OTAManager」的标准调用流程,可作为另外两种连接方式的调用范式参考(连接方式层不同,OTA 层调用一致)。
Swift 示例:连接与升级主流程
import UIKit
import CoreBluetooth
import JL_OTALib
/// OTA 示例控制器:演示 JL_Assist 自定义蓝牙连接与 JL_OTAManager 的标准调用流程
final class OTAExampleViewController: UIViewController, JL_OTAManagerDelegate {
private let otaManager = JL_OTAManager.getOTAManager() // 单例获取
override func viewDidLoad() {
super.viewDidLoad()
// 1) 绑定 delegate
otaManager.delegate = self
// 2) 扫描并连接设备
BleManager.shared.startScan()
// 选择设备后:BleManager.shared.connect(peripheral: sel)
}
// 3) 订阅完成后初始化 OTA 上下文
private func onPeripheralReady(_ peripheral: CBPeripheral) {
otaManager.mBLE_UUID = peripheral.identifier.uuidString
otaManager.mBLE_NAME = peripheral.name ?? ""
otaManager.noteEntityConnected() // 通知 OTA 层设备已就绪
otaManager.cmdTargetFeature() // 查询设备升级能力
}
// 4) 发起 OTA 升级
private func startUpgrade(with data: Data) {
otaManager.cmdOTAData(data)
}
// 5) 取消升级
private func cancelUpgrade() {
otaManager.cmdOTACancelResult { result in
print("OTA 取消:\(result)")
}
otaManager.noteEntityDisconnected()
}
// 6) 委托回调
func otaUpgradeResult(_ result: JL_OTAResult, progress: Float) {
print("升级状态:\(result) 进度:\(progress)")
}
func otaDataSend(_ data: Data) {
BleManager.shared.assistManager.bleWrite(data) // 桥接写入既有蓝牙层
}
func otaCancel() {}
func otaFeatureResult(_ manager: JL_OTAManager) {}
}
Source: OTA 升级开发示例(JL_Assist 自定义蓝牙连接).md#L9-L61
Objective-C 示例:回连与超时重试策略
#import <JL_OTALib/JL_OTALib.h>
/**
异常与重试策略示例(Objective‑C)
展示基于自定义连接的回连与超时重试的基本处理
*/
@interface OTARetryGuideObjC : NSObject <JL_OTAManagerDelegate>
@property (nonatomic, strong) JL_OTAManager *otaManager;
@property (nonatomic, assign) NSInteger retryCount;
@end
@implementation OTARetryGuideObjC
- (instancetype)init {
if (self = [super init]) {
_otaManager = [JL_OTAManager getOTAManager];
_retryCount = 0;
_otaManager.delegate = self;
}
return self;
}
- (void)otaUpgradeResult:(JL_OTAResult)result Progress:(float)progress {
switch (result) {
case JL_OTAResultReconnect:
case JL_OTAResultReconnectUpdateSource: {
// 按 UUID 回连
NSString *uuid = self.otaManager.mBLE_UUID;
[[BleManager shared] reConnectWithUUID:uuid];
break;
}
case JL_OTAResultReconnectWithMacAddr: {
// 按 MAC 地址回连
NSString *mac = self.otaManager.bleAddr;
[[BleManager shared] reConnectWithMac:mac];
break;
}
case JL_OTAResultFailCmdTimeout: {
// 命令超时:指数退避重试 cmdTargetFeature,最多 3 次
if (self.retryCount < 3) {
self.retryCount += 1;
NSTimeInterval delay = (NSTimeInterval)self.retryCount;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.otaManager cmdTargetFeature];
});
}
break;
}
default:
break;
}
}
@end
Source: OTA 升级开发示例(JL_Assist 自定义蓝牙连接).md#L63-L117
两个示例共同揭示了连接方式层与 OTA 层的协作契约:连接方式层只需兑现「能写入、能收数据、能断开」三个能力,其余升级逻辑(分包、校验、状态机、回连触发)全部由 JL_OTAManager 完成。这也解释了为何三种连接方式可以共享同一套 JL_OTAManager API。
配置选项
连接方式的接入不需要额外参数,但无论选择哪种方式,都必须完成以下工程配置(见 README.md#L100-L105):
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
JL_OTALib.xcframework | 框架依赖 | 必选 | OTA 核心能力库(JL_OTAManager),需 Embed & Sign |
JL_AdvParse.xcframework | 框架依赖 | 必选 | 广播包解析库 |
JL_HashPair.xcframework | 框架依赖 | 必选 | Hash 配对认证库 |
JLLogHelper.xcframework | 框架依赖 | 必选 | 日志辅助库 |
Privacy - Bluetooth Peripheral Usage Description | Info.plist 权限 | 必填 | iOS 12.0+ 使用 BLE 必需 |
Privacy - Bluetooth Always Usage Description | Info.plist 权限 | 必填 | 后台/常驻蓝牙场景必需 |
mBLE_UUID | JL_OTAManager 属性 | 空 | 已连接设备 UUID,重连依据 |
mBLE_NAME | JL_OTAManager 属性 | 空 | 设备名,用于日志与回连 |
bleAddr | JL_OTAManager 属性 | 空 | 设备 MAC 地址,MAC 回连依据 |
运行环境要求(见 README.md#L58-L66):iOS 12.0+、Xcode 14.0+、支持 RCSP 协议的固件(AC695X、AC697X 等 SDK)、Objective-C / Swift 均可。
失败模式、边界情况与并发注意
从上述 Objective-C 示例可以归纳出 SDK 暴露的主要失败模式及推荐处理方式:
| 失败模式 | 触发场景 | 推荐处理 |
|---|---|---|
JL_OTAResultReconnect | 升级过程中设备断开,需要按 UUID 回连 | 读取 mBLE_UUID 调用 reConnectWithUUID: |
JL_OTAResultReconnectUpdateSource | 升级中断且需要更新升级源后回连 | 与上类似,按 UUID 回连 |
JL_OTAResultReconnectWithMacAddr | 设备地址变化/需按 MAC 回连 | 读取 bleAddr 调用 reConnectWithMac: |
JL_OTAResultFailCmdTimeout | 命令发送后超时(设备未响应) | 有限次数重试 cmdTargetFeature,退避延时随次数递增 |
边界情况与并发注意:
- 时序边界:
noteEntityConnected必须在特征订阅完成后调用;过早调用会导致升级数据包无法送达设备。 - 重连状态机:SDK 以
mBLE_UUID/mBLE_NAME/bleAddr作为回连身份依据,切换设备或清空上下文前必须noteEntityDisconnected,否则旧上下文可能被新连接复用。 - 写入并发:
otaDataSend回调要求连接方式层按序写入;若底层写通道支持无响应写(writeWithoutResponse),SDK 的分包节奏仍要求开发者保证队列 FIFO,避免乱序导致升级包校验失败。 - 超时重试:
JL_OTAResultFailCmdTimeout的重试需限制次数(示例中为 3 次)并配合退避,防止设备端处于升级异常状态时无限重发造成信道拥塞。 - 取消语义:
cmdOTACancelResult完成后应调用noteEntityDisconnected收尾,确保 OTA 上下文与蓝牙连接状态一致。
性能与运维提示
- 升级耗时主要取决于 MTU 与写通道吞吐:原生 CoreBluetooth 方式可自行协商 MTU/选择写入类型,吞吐上限最高;JL_BLEKit 与 JL_Assist 方式受 SDK/外部层封装约束。
- 日志先行:集成
JLLogHelper.xcframework,排查升级失败时优先核对noteEntityConnected→cmdTargetFeature→cmdOTAData的调用顺序与otaDataSend是否持续被驱动。 - 真机验证:BLE 行为(扫描、连接、后台订阅)依赖真实硬件,模拟器无法完整验证连接方式层,选型后应在真机上完成回连与断线场景测试。
扩展点
- 桥接层(JL_Assist):
assistManager.bleWrite(data)是唯一的写入出口,开发者可在其中加入写入队列、日志埋点、流量统计,而无需改动 OTA 层。 - 自定义 BleManager(原生方式):
code/JL_OTA/BleManager/提供参考实现,可在此基础上扩展多设备管理、扫描过滤、连接参数配置。 - 委托回调:
JL_OTAManagerDelegate(otaUpgradeResult、otaDataSend、otaCancel、otaFeatureResult)是观察 OTA 状态机的标准扩展接口,可在此基础上实现升级进度 UI、埋点上报、回连提示。