扬庆の博客

SDK 硬件设备接入:从蓝牙扫描、绑定到自动重连的完整链路

字数统计: 3.5k阅读时长: 12 min
2026/06/18 Share

SDK 硬件设备接入:从蓝牙扫描、绑定到自动重连的完整链路

移动端接入 BLE 硬件时,最容易出问题的地方通常不是单次 connect 调用,而是整条链路是否闭合:扫描前权限是否就绪、发现结果是否去重、点击绑定时是否停掉扫描、SDK ready 与业务绑定是否分层、连接状态是否能回流到设备列表,以及断开后是否能按节奏自动重连。

本文基于一个 iOS 侧 SDK 对接实践,抽象出一套可复用的技术流程。文中不依赖具体项目、产品名称或私有接口,只讨论蓝牙连接、SDK 会话和业务状态编排。

一、整体架构

建议把硬件接入拆成三层:

  1. 页面编排层
    负责权限说明、扫描动画、扫描结果展示、点击设备、绑定 loading、错误提示和页面跳转。

  2. 设备协调层
    负责把页面事件转成 SDK 行为,例如开始扫描、停止扫描、连接设备、断开设备、解绑设备、读取当前连接快照。

  3. SDK 连接层
    负责 CoreBluetooth 扫描、GATT 连接、Service/Characteristic 发现、Notify 订阅、协议握手、数据收发、连接状态回调。

这三层不要互相越界。页面不直接操作 CBCentralManager,SDK 不关心业务绑定接口,设备列表不直接读取 SDK 内部对象,而是通过统一的连接态查询能力拿结果。

推荐事件流如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
进入添加设备页
-> 检查登录态、蓝牙权限、系统蓝牙开关
-> 初始化对应设备 SDK
-> 开始扫描
-> SDK 回调发现设备
-> 页面展示去重后的设备列表
-> 用户点击某个设备
-> 停止扫描并取消其它 pending 连接
-> SDK 连接设备
-> GATT ready / 协议 ready
-> 调业务绑定接口
-> 绑定成功后持久化本地连接标识
-> 刷新设备列表并展示已连接
-> 后续断开或蓝牙恢复时触发自动重连

二、扫描前的蓝牙门禁

BLE 扫描前需要先做门禁判断,否则用户会看到不稳定的扫描状态,甚至系统弹窗时机混乱。

扫描前至少判断:

  • 用户是否已登录或已满足业务前置条件。
  • App 是否有蓝牙权限。
  • 系统蓝牙是否开启。
  • 当前设备是否支持 BLE。
  • 是否正处于权限说明弹窗阶段。
  • 是否已有其它设备 SDK 正在独占扫描或连接。

iOS 上需要特别注意 CBCentralManager.state == .unknown 的短暂状态。刚初始化 central manager 时,系统可能还没回调真实状态,此时不要立刻提示“蓝牙未开启”。更稳妥的做法是先进入等待态,收到 .poweredOn 后再启动扫描,收到 .poweredOff 或权限拒绝后再提示用户。

三、扫描与发现展示

扫描一般由 SDK 层执行,页面层只消费扫描结果。SDK 层可以按私有 Service UUID 扫描,也可以在某些双模设备场景下先读取系统已连接且暴露目标 Service 的外设。

典型 SDK 扫描步骤:

1
2
3
4
5
6
检查 central.state == poweredOn
-> scanForPeripherals(withServices: [目标 Service])
-> 设置扫描超时
-> didDiscover peripheral
-> 解析广播名、外设 UUID、广播 manufacturer data
-> 推送设备摘要到业务层

设备摘要建议只暴露展示和连接所需字段:

  • 蓝牙广播名。
  • 外设 UUID。
  • BLE MAC 或经典蓝牙 MAC,如果能从广播或协议中解析出来。
  • 设备栈类型或设备品类标识。

页面展示前还要做几件事:

  • 按外设 UUID、MAC、蓝牙名合并重复结果。
  • 过滤已绑定设备,避免重复添加。
  • 按手动添加页的目标型号过滤结果。
  • 只展示当前业务可处理的设备类型。
  • 扫描结束后如果没有发现目标设备,给出靠近设备或重试提示。

扫描时间不宜过长。实践中常见做法是 10 到 15 秒一轮扫描,用户可手动重试。页面离开时必须停止扫描,避免多个页面或多个 SDK 抢占 BLE 资源。

四、点击绑定:先连 BLE,再绑业务

用户点击扫描结果后,不应马上调用业务绑定接口。正确顺序是先完成 BLE 连接和 SDK ready,再执行业务绑定。

点击设备后的关键步骤:

1
2
3
4
5
6
7
8
9
点击扫描结果
-> 判断是否已绑定
-> 设置 userInitiatedBind 标记
-> 显示绑定 loading
-> 停止当前扫描
-> 取消其它设备通道的 pending 连接
-> 用外设 UUID 或 MAC 调 SDK connect
-> 等待 SDK ready / 协议 ready
-> ready 后调用业务绑定接口

这里的 userInitiatedBind 很重要。部分 SDK 在连接前会先断开旧连接或重置内部会话,如果页面把这类“准备连接过程中的断开”当成失败,会导致用户点击绑定后立刻失败。因此需要用“用户主动绑定中”的状态区分真正失败和连接前置清理。

连接超时也需要单独控制。可以设置 15 秒左右的连接超时:如果超时仍未 ready,就主动断开 SDK 会话,清理 pending 状态,并提示用户重新连接。

五、SDK 连接层:GATT ready 与协议 ready

SDK 连接层通常包含两段 ready:

  1. GATT ready
    手机已经连接外设,发现了目标 Service,找到了 Write 和 Notify Characteristic,并成功开启 Notify。

  2. 协议 ready
    在 GATT 通道上完成设备协议要求的地址解析、握手、鉴权或初始化指令,SDK 可以稳定收发业务命令。

典型 GATT 流程:

1
2
3
4
5
6
7
8
9
central.connect(peripheral)
-> didConnect
-> discoverServices([目标 Service])
-> didDiscoverServices
-> discoverCharacteristics([Write, Notify])
-> didDiscoverCharacteristics
-> setNotifyValue(true)
-> didUpdateNotificationState
-> GATT ready

GATT ready 后,协议层可能还要继续做:

  • 从 Notify 数据中解析设备地址。
  • 生成握手明文或加密数据。
  • 发送握手指令。
  • 等待设备响应。
  • 启动心跳或读取设备信息。

不同 SDK 对“连接成功”的定义不一样。有些 SDK 把 GATT ready 就视为连接成功,有些必须等协议握手完成才算真正 ready。业务层不要只看 didConnect,而应统一使用 SDK 暴露的 final ready 状态,例如 isReadyisHandshakeCompleted 或 connection state 的 connected 值。

六、绑定成功后的本地持久化

业务绑定接口成功后,需要把“下次能重连”和“列表能显示已连接”的必要信息写入本地。

建议持久化:

  • 外设 UUID:用于下次直接 retrieve/connect。
  • 设备 MAC:用于业务设备列表和当前 SDK 会话做匹配。
  • 最近连接时间:用于列表展示。
  • 最近电量:用于设备卡片展示。
  • 连接偏好:用于刚绑定成功后列表立即显示为已连接。

注意:业务绑定接口成功不等于 SDK 可以断开。刚绑定成功时如果立刻调用 disconnect,用户返回设备列表会看到刚添加的设备变成未连接。更合理的做法是结束“添加设备独占会话”,把当前连接交还给主页或全局会话管理,而不是断链。

七、设备列表连接态展示

设备列表不应该直接依赖某个 SDK 对象。更好的方式是定义一个统一的连接态查询入口,由各设备业务在初始化时注入判断逻辑。

列表展示时只问两个问题:

  • 这个列表项当前是否显示已连接?
  • 这个列表项是否已经满足重连条件,可以跳过自动重连?

判断时要比对当前 SDK 会话与列表项是否为同一台设备。常见比对字段包括:

  • 业务设备 MAC。
  • 外设 UUID。
  • 设备序列号。
  • 绑定时保存的经典蓝牙 MAC。
  • 蓝牙广播名兜底。

不能只用“SDK 当前有连接”来展示所有同类设备已连接,否则用户绑定多个同类设备时会出现状态串台。

八、自动重连机制

自动重连建议由全局协调器统一调度,而不是每个页面自己写一套。

触发自动重连的时机:

  • 设备列表页可见,并注入了当前账号下的可重连设备快照。
  • App 从后台回到前台。
  • 系统蓝牙从关闭变为开启。
  • SDK 收到非用户主动的断开事件。
  • 周期性心跳检查发现目标设备未连接。

自动重连前要先过滤:

  • 用户未登录时不初始化 SDK,避免登录页触发系统蓝牙权限弹窗。
  • 当前账号没有已绑定 BLE 设备时不安装重连协调器。
  • 设备已被删除或加入本地删除黑名单时不重连。
  • 添加设备会话中的断开不触发全局重连。
  • 用户主动解绑或主动断开不触发自动重连。
  • 当前列表项已经满足连接态时跳过。

重连调度策略建议:

1
2
3
4
5
6
7
8
9
设备快照进入协调器
-> 按设备类型构建重连目标
-> 判断当前是否已连接
-> 未连接则入队
-> 限制并发数
-> 调设备 SDK 的 auto reconnect / connect
-> 延迟读取统一连接态作为结果
-> 成功则清空退避
-> 失败则指数退避后重新入队

实践中可以做一个 5 分钟左右的心跳巡检,再配合断开事件即时触发。为了避免冷启动时蓝牙状态、登录态、SDK 初始化互相抢时机,可以在 App 启动后的前几秒延迟 flush 重连队列。

失败退避可以用指数退避加少量随机抖动,例如 2 秒、4 秒、8 秒、16 秒,最大不超过 60 秒。这样可以避免设备离线时持续占用 BLE 和电量。

九、解绑与删除

删除业务设备前,最好先通过 SDK 执行解绑或断开,清理本地绑定标识,再调用业务删除接口。即使 SDK 解绑失败,业务删除也可以继续,但本地要做好两类保护:

  • 清理本地保存的外设 UUID、MAC、连接偏好。
  • 将刚删除的设备短暂加入删除黑名单,避免全局自动重连马上把它连回来。

解绑流程建议:

1
2
3
4
5
6
用户删除设备
-> SDK unbind / disconnect
-> 清理本地连接标识
-> 调业务删除接口
-> 刷新设备列表
-> 自动重连协调器剔除该设备

十、常见问题与处理建议

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 层的整体编排可以保持稳定复用。

CATALOG
  1. 1. SDK 硬件设备接入:从蓝牙扫描、绑定到自动重连的完整链路
    1. 1.1. 一、整体架构
    2. 1.2. 二、扫描前的蓝牙门禁
    3. 1.3. 三、扫描与发现展示
    4. 1.4. 四、点击绑定:先连 BLE,再绑业务
    5. 1.5. 五、SDK 连接层:GATT ready 与协议 ready
    6. 1.6. 六、绑定成功后的本地持久化
    7. 1.7. 七、设备列表连接态展示
    8. 1.8. 八、自动重连机制
    9. 1.9. 九、解绑与删除
    10. 1.10. 十、常见问题与处理建议
      1. 1.10.1. 1. 扫描不到设备
      2. 1.10.2. 2. 发现了设备但点击绑定失败
      3. 1.10.3. 3. 刚连上又变未连接
      4. 1.10.4. 4. 设备列表连接态串台
      5. 1.10.5. 5. 自动重连反复打扰用户
      6. 1.10.6. 6. 删除后又自动连回来了
    11. 1.11. 十一、推荐的落地清单
    12. 1.12. 总结