SDK 硬件设备接入:从蓝牙扫描、绑定到自动重连的完整链路
移动端接入 BLE 硬件时,最容易出问题的地方通常不是单次 connect 调用,而是整条链路是否闭合:扫描前权限是否就绪、发现结果是否去重、点击绑定时是否停掉扫描、SDK ready 与业务绑定是否分层、连接状态是否能回流到设备列表,以及断开后是否能按节奏自动重连。
本文基于一个 iOS 侧 SDK 对接实践,抽象出一套可复用的技术流程。文中不依赖具体项目、产品名称或私有接口,只讨论蓝牙连接、SDK 会话和业务状态编排。
一、整体架构
建议把硬件接入拆成三层:
页面编排层
负责权限说明、扫描动画、扫描结果展示、点击设备、绑定 loading、错误提示和页面跳转。设备协调层
负责把页面事件转成 SDK 行为,例如开始扫描、停止扫描、连接设备、断开设备、解绑设备、读取当前连接快照。SDK 连接层
负责 CoreBluetooth 扫描、GATT 连接、Service/Characteristic 发现、Notify 订阅、协议握手、数据收发、连接状态回调。
这三层不要互相越界。页面不直接操作 CBCentralManager,SDK 不关心业务绑定接口,设备列表不直接读取 SDK 内部对象,而是通过统一的连接态查询能力拿结果。
推荐事件流如下:
1 | 进入添加设备页 |
二、扫描前的蓝牙门禁
BLE 扫描前需要先做门禁判断,否则用户会看到不稳定的扫描状态,甚至系统弹窗时机混乱。
扫描前至少判断:
- 用户是否已登录或已满足业务前置条件。
- App 是否有蓝牙权限。
- 系统蓝牙是否开启。
- 当前设备是否支持 BLE。
- 是否正处于权限说明弹窗阶段。
- 是否已有其它设备 SDK 正在独占扫描或连接。
iOS 上需要特别注意 CBCentralManager.state == .unknown 的短暂状态。刚初始化 central manager 时,系统可能还没回调真实状态,此时不要立刻提示“蓝牙未开启”。更稳妥的做法是先进入等待态,收到 .poweredOn 后再启动扫描,收到 .poweredOff 或权限拒绝后再提示用户。
三、扫描与发现展示
扫描一般由 SDK 层执行,页面层只消费扫描结果。SDK 层可以按私有 Service UUID 扫描,也可以在某些双模设备场景下先读取系统已连接且暴露目标 Service 的外设。
典型 SDK 扫描步骤:
1 | 检查 central.state == poweredOn |
设备摘要建议只暴露展示和连接所需字段:
- 蓝牙广播名。
- 外设 UUID。
- BLE MAC 或经典蓝牙 MAC,如果能从广播或协议中解析出来。
- 设备栈类型或设备品类标识。
页面展示前还要做几件事:
- 按外设 UUID、MAC、蓝牙名合并重复结果。
- 过滤已绑定设备,避免重复添加。
- 按手动添加页的目标型号过滤结果。
- 只展示当前业务可处理的设备类型。
- 扫描结束后如果没有发现目标设备,给出靠近设备或重试提示。
扫描时间不宜过长。实践中常见做法是 10 到 15 秒一轮扫描,用户可手动重试。页面离开时必须停止扫描,避免多个页面或多个 SDK 抢占 BLE 资源。
四、点击绑定:先连 BLE,再绑业务
用户点击扫描结果后,不应马上调用业务绑定接口。正确顺序是先完成 BLE 连接和 SDK ready,再执行业务绑定。
点击设备后的关键步骤:
1 | 点击扫描结果 |
这里的 userInitiatedBind 很重要。部分 SDK 在连接前会先断开旧连接或重置内部会话,如果页面把这类“准备连接过程中的断开”当成失败,会导致用户点击绑定后立刻失败。因此需要用“用户主动绑定中”的状态区分真正失败和连接前置清理。
连接超时也需要单独控制。可以设置 15 秒左右的连接超时:如果超时仍未 ready,就主动断开 SDK 会话,清理 pending 状态,并提示用户重新连接。
五、SDK 连接层:GATT ready 与协议 ready
SDK 连接层通常包含两段 ready:
GATT ready
手机已经连接外设,发现了目标 Service,找到了 Write 和 Notify Characteristic,并成功开启 Notify。协议 ready
在 GATT 通道上完成设备协议要求的地址解析、握手、鉴权或初始化指令,SDK 可以稳定收发业务命令。
典型 GATT 流程:
1 | central.connect(peripheral) |
GATT ready 后,协议层可能还要继续做:
- 从 Notify 数据中解析设备地址。
- 生成握手明文或加密数据。
- 发送握手指令。
- 等待设备响应。
- 启动心跳或读取设备信息。
不同 SDK 对“连接成功”的定义不一样。有些 SDK 把 GATT ready 就视为连接成功,有些必须等协议握手完成才算真正 ready。业务层不要只看 didConnect,而应统一使用 SDK 暴露的 final ready 状态,例如 isReady、isHandshakeCompleted 或 connection state 的 connected 值。
六、绑定成功后的本地持久化
业务绑定接口成功后,需要把“下次能重连”和“列表能显示已连接”的必要信息写入本地。
建议持久化:
- 外设 UUID:用于下次直接 retrieve/connect。
- 设备 MAC:用于业务设备列表和当前 SDK 会话做匹配。
- 最近连接时间:用于列表展示。
- 最近电量:用于设备卡片展示。
- 连接偏好:用于刚绑定成功后列表立即显示为已连接。
注意:业务绑定接口成功不等于 SDK 可以断开。刚绑定成功时如果立刻调用 disconnect,用户返回设备列表会看到刚添加的设备变成未连接。更合理的做法是结束“添加设备独占会话”,把当前连接交还给主页或全局会话管理,而不是断链。
七、设备列表连接态展示
设备列表不应该直接依赖某个 SDK 对象。更好的方式是定义一个统一的连接态查询入口,由各设备业务在初始化时注入判断逻辑。
列表展示时只问两个问题:
- 这个列表项当前是否显示已连接?
- 这个列表项是否已经满足重连条件,可以跳过自动重连?
判断时要比对当前 SDK 会话与列表项是否为同一台设备。常见比对字段包括:
- 业务设备 MAC。
- 外设 UUID。
- 设备序列号。
- 绑定时保存的经典蓝牙 MAC。
- 蓝牙广播名兜底。
不能只用“SDK 当前有连接”来展示所有同类设备已连接,否则用户绑定多个同类设备时会出现状态串台。
八、自动重连机制
自动重连建议由全局协调器统一调度,而不是每个页面自己写一套。
触发自动重连的时机:
- 设备列表页可见,并注入了当前账号下的可重连设备快照。
- App 从后台回到前台。
- 系统蓝牙从关闭变为开启。
- SDK 收到非用户主动的断开事件。
- 周期性心跳检查发现目标设备未连接。
自动重连前要先过滤:
- 用户未登录时不初始化 SDK,避免登录页触发系统蓝牙权限弹窗。
- 当前账号没有已绑定 BLE 设备时不安装重连协调器。
- 设备已被删除或加入本地删除黑名单时不重连。
- 添加设备会话中的断开不触发全局重连。
- 用户主动解绑或主动断开不触发自动重连。
- 当前列表项已经满足连接态时跳过。
重连调度策略建议:
1 | 设备快照进入协调器 |
实践中可以做一个 5 分钟左右的心跳巡检,再配合断开事件即时触发。为了避免冷启动时蓝牙状态、登录态、SDK 初始化互相抢时机,可以在 App 启动后的前几秒延迟 flush 重连队列。
失败退避可以用指数退避加少量随机抖动,例如 2 秒、4 秒、8 秒、16 秒,最大不超过 60 秒。这样可以避免设备离线时持续占用 BLE 和电量。
九、解绑与删除
删除业务设备前,最好先通过 SDK 执行解绑或断开,清理本地绑定标识,再调用业务删除接口。即使 SDK 解绑失败,业务删除也可以继续,但本地要做好两类保护:
- 清理本地保存的外设 UUID、MAC、连接偏好。
- 将刚删除的设备短暂加入删除黑名单,避免全局自动重连马上把它连回来。
解绑流程建议:
1 | 用户删除设备 |
十、常见问题与处理建议
1. 扫描不到设备
先检查蓝牙权限、系统蓝牙开关、Service UUID 是否正确、设备是否处于广播模式、扫描过滤条件是否过窄。双模设备还要检查系统已连接外设是否能通过 Service retrieve 到。
2. 发现了设备但点击绑定失败
区分是 GATT 连接失败、Characteristic 未发现、Notify 未开启、协议握手失败,还是业务绑定接口失败。不要把所有失败都显示成“添加失败”,日志和错误码要能定位到层级。
3. 刚连上又变未连接
检查绑定成功后是否误调用了 disconnect,或者添加设备页销毁时是否把已成功连接的会话也清掉。绑定成功后的目标应是“结束添加设备会话并保留连接”。
4. 设备列表连接态串台
检查列表连接态是否只判断“SDK 是否有连接”。应改成“SDK 已连接且当前会话标识匹配该列表项”。
5. 自动重连反复打扰用户
检查是否在未登录、无绑定设备、登录页、隐私同意前初始化 SDK。自动重连应由设备快照驱动,而不是 App 启动就无条件初始化所有 SDK。
6. 删除后又自动连回来了
检查删除流程是否清理本地绑定标识,以及自动重连协调器是否过滤了已删除设备。删除、解绑、重连三者必须共用同一套设备标识规范化逻辑。
十一、推荐的落地清单
- 扫描前统一做蓝牙权限和开关门禁。
- 扫描结果统一摘要化,只向页面暴露 name、uuid、mac、stack 等必要字段。
- 扫描结果进入页面前做去重、已绑定过滤和型号过滤。
- 点击绑定时先停扫,再取消其它通道 pending 连接。
- BLE ready 后再调用业务绑定接口。
- 绑定成功后保留 SDK 连接,并持久化 UUID、MAC、连接时间、电量和连接偏好。
- 设备列表通过统一 Hook 查询连接态,不直接依赖具体 SDK。
- 自动重连由全局协调器调度,支持前台、蓝牙恢复、断开事件和心跳触发。
- 自动重连必须有并发限制、冷启动延迟和失败退避。
- 删除设备时先解绑/断开并清理本地标识,再刷新列表和重连目标。
总结
BLE 硬件 SDK 接入的核心不是“能不能扫描到、能不能 connect”,而是要把扫描、发现、点击绑定、业务持久化、列表展示和自动重连串成一个闭环。
一个稳定的接入方案应满足:
- 扫描结果可信。
- 连接 ready 定义清晰。
- 业务绑定与 BLE 连接分层。
- 本地绑定标识可用于展示和重连。
- 列表状态只命中当前设备。
- 断开后能克制、有节奏地重连。
- 用户主动删除、解绑、退出页面等动作不会被自动重连抵消。
只要这条状态链清楚,后续接入不同厂商 SDK 时,差异通常只会落在扫描过滤、连接 ready 判定和协议握手细节上,App 层的整体编排可以保持稳定复用。