第1章 边界定义与依赖注入引入动机
1.1 BLE并发模式概览
在嵌入式与移动端开发中,BLE通信的并发场景并非单一模型。根据连接数量与数据流方向,存在三种典型并发模式。理解这些模式是识别依赖注入边界的前提。
| 模式 | 并发能力 | 典型场景 | 关键约束 |
|---|---|---|---|
| 并发连接 | 同时维护2~20个连接 | 中央设备同时读取多个外设传感器 | 连接间隔、调度延迟、缓冲区争用 |
| 并发扫描+连接 | 扫描间隔与连接事件交错 | 设备发现后立即建立连接 | 扫描窗口与连接窗重叠导致丢包 |
| 多服务并发 | 单连接下多特征值并行读写 | OTA升级同时传输实时数据 | ATT层MTU限制、优先级反转 |
注意:以上三种模式在实际系统中往往叠加出现。例如,一个IoT网关可能同时保持6个并发连接,每个连接内部又存在多个特征值通知通道。多级并发使得代码中原本隐式的状态依赖迅速膨胀。
1.2 依赖注入引入动机
传统BLE开发通常将蓝牙适配器、GATT回调、连接状态机直接固化在业务模块内部。当并发连接数超过4个时,这种紧耦合会导致以下顽疾:
- 回调地狱:多个并发的
onConnectionStateChange与onCharacteristicRead在同一个线程中交织,状态机条件判断分支爆炸。 - 难以Mock:单元测试需要物理蓝牙设备或模拟器,本地测试周期从秒级退化到分钟级。
- 配置硬编码:连接参数(扫描间隔、连接间隔、MTU大小)写死在初始化代码中,切换场景需要重新编译。
引入依赖注入后,上述横切关注点被提升为可替换的抽象层。核心转变是将“如何获得BLE服务”与“如何使用BLE服务”分离。
依赖注入在此场景的核心价值:将并发BLE资源的生命周期管理从业务逻辑中剥离,使连接池、重试策略、调度策略成为可注入的依赖,而非内嵌逻辑。
1.3 边界定义的三层模型
根据经验抽象,我们将并发BLE系统的依赖注入边界划分为三个层次。每一层拥有独立的生命周期与变更频率。
| 层次 | 职责 | 边界特征 | 典型依赖注入点 |
|---|---|---|---|
| 设备层 | 封装硬件适配、HCI指令、物理连接生命周期 | 由芯片/OS SDK决定,变更频率低 | BluetoothAdapter、GattServer |
| 服务层 | 管理连接池、GATT操作队列、广播/扫描控制器 | 由并发策略决定,变更频率中 | ConnectionPool、GattOperationQueue |
| 业务层 | 实现具体功能:数据采集、OTA、配网协议 | 由产品需求驱动,变更频率高 | SensorDataHandler、FirmwareUpgrader |
三层之间的依赖方向必须是业务层→服务层→设备层,禁止逆向依赖。依赖注入容器负责在运行时将设备层和服务层的具体实现注入到业务层中。
1.4 控制流与数据流设计
在三层模型中,控制流与数据流的路径不同:
- 控制流:从上往下注入。业务层启动时通过DI容器获取服务层的连接池句柄,服务层依赖设备层的蓝牙适配器。启动顺序由DI容器保证。
- 数据流:从下往上回调。设备层的GATT回调将原始数据包推送到服务层,服务层解析并分发给对应的业务模块。此处通常使用观察者模式或响应式流作为解耦手段。
// 数据流示例:服务层向业务层分发特征值通知
class GattServiceManager {
@Inject private Map<UUID, CharacteristicHandler> handlerMap;
void onCharacteristicUpdated(BluetoothDevice device,
int status,
byte[] value) {
UUID charUuid = extractUuid(value);
CharacteristicHandler h = handlerMap.get(charUuid);
if (h != null) {
h.onDataReceived(device, value);
}
}
}
1.5 选型权衡与风险点
| 维度 | 权衡内容 | 风险等级 |
|---|---|---|
| 抽象粒度 | 细粒度注入(每个特征值一个接口) vs 粗粒度注入(整个服务层一个接口) | 高 |
| 生命周期管理 | DI容器管理连接池 vs 业务层自行管理连接的打开/关闭 | 中 |
| 测试替代方案 | Mock框架(Mockito) vs 手工Stub实现 | 低 |
风险点:过度抽象会导致连接控制权模糊,例如当多个业务模块同时注入同一个连接池时,关闭操作的时序需要严格约定。建议引入引用计数或弱引用策略来管理共享依赖的释放。
1.6 落地步骤与验收标准
- 步骤一:边界识别——从现有BLE代码中提取所有与硬件、连接、GATT操作相关的方法,归类到设备层或服务层。
- 步骤二:接口定义——为设备层和服务层分别定义纯抽象接口(Interface),接口方法参数必须全部是平台无关类型。
- 步骤三:注入点确定——在业务模块的构造函数或工厂方法中添加
@Inject注解(或等价的配置声明)。 - 步骤四:模块化编译——确保设备层、服务层、业务层分属不同的编译单元(.so / .jar / .module)。
- 步骤五:测试验证——编写至少3个针对服务层的单元测试,使用Mock桩替代设备层依赖。
验收标准:
- 业务层代码中不出现任何
BluetoothAdapter、BluetoothGatt、BluetoothDevice的直接引用。 - 替换底层蓝牙实现(例如从Android BLE切换至BlueZ)时,仅需替换设备层的DI绑定配置,业务层代码零修改。
- 并发连接数从4个扩展到8个时,服务层的连接池配置可独立修改,无需改动其他两层。