2. 核心接口定义:IScanner、IConnector、IServiceManager 接口设计
在蓝牙扫描-连接-服务分离架构中,三个顶层接口承担着职责解耦的核心角色。IScanner 负责设备发现与广播监听,IConnector 负责建立/断开连接及连接参数管理,IServiceManager 负责服务发现、属性读写及通知订阅。三者彼此独立,仅通过统一的回调或异步事件总线通信,保证模块可单独替换测试。
2.1 接口职责与设计原则
| 接口 | 核心职责 | 设计原则 |
|---|---|---|
| IScanner | 启动/停止扫描,过滤设备,回调发现结果 | 无状态、可随时重新扫描;只暴露 BLE 状态无关的扫描参数 |
| IConnector | 建立连接、断开连接、管理连接优先级与 MTU | 面向连接生命周期;通过连接句柄标识通道 |
| IServiceManager | 发现服务/特征/描述符,读写属性,注册通知 | 面向 GATT 层级;不依赖扫描状态 |
2.2 接口定义(C++ 类风格示例)
// IScanner.h
class IScanner {
public:
virtual ~IScanner() = default;
virtual void StartScan(const ScanFilter& filter, uint32_t timeout_ms) = 0;
virtual void StopScan() = 0;
// 结果通过回调传入
using ScanResultCallback = std::function<void(const DeviceInfo& dev)>;
virtual void SetScanResultCallback(ScanResultCallback cb) = 0;
};
// IConnector.h
class IConnector {
public:
virtual ~IConnector() = default;
virtual void Connect(const DeviceAddress& addr, uint16_t timeout_ms) = 0;
virtual void Disconnect(const ConnectionHandle& handle) = 0;
virtual void RequestConnectionPriority(const ConnectionHandle& handle, ConnectionPriority prio) = 0;
// 连接状态回调
using ConnectionStateCallback = std::function<void(const ConnectionHandle& handle, ConnectionState state)>;
virtual void SetConnectionStateCallback(ConnectionStateCallback cb) = 0;
};
// IServiceManager.h
class IServiceManager {
public:
virtual ~IServiceManager() = default;
virtual void DiscoverServices(const ConnectionHandle& handle) = 0;
virtual void ReadCharacteristic(const ConnectionHandle& handle, const AttributeHandle& attr) = 0;
virtual void WriteCharacteristic(const ConnectionHandle& handle, const AttributeHandle& attr,
const std::vector<uint8_t>& data, WriteType type) = 0;
virtual void EnableNotification(const ConnectionHandle& handle, const AttributeHandle& attr,
bool enable) = 0;
// 数据及服务变更回调
using ServiceDiscoveredCallback = std::function<void(const ConnectionHandle& handle,
const ServiceList& services)>;
using AttributeDataCallback = std::function<void(const ConnectionHandle& handle,
const AttributeHandle& attr, const std::vector<uint8_t>& data)>;
virtual void SetServiceDiscoveredCallback(ServiceDiscoveredCallback cb) = 0;
virtual void SetAttributeDataCallback(AttributeDataCallback cb) = 0;
};
2.3 接口关系与注入流程
三个接口通过依赖注入(DI)组装到上层业务模块。业务层仅持有接口指针,不依赖具体实现。下图展示接口之间的协作关系与典型数据流:
2.4 实现流程与策略
- 定义接口纯抽象类(如上代码段),放入公共头文件目录。
- 为每个接口提供默认实现(如 Android 下的
AndroidScanner、AndroidConnector),同一接口可同时存在多个实现(模拟实现、基于不同蓝牙栈的实现)。 - 使用依赖注入容器(如手动工厂或轻量 DI 框架)在应用启动时组装:
// 伪代码:工厂创建 auto scanner = std::make_shared<AndroidScanner>(); auto connector = std::make_shared<AndroidConnector>(); auto serviceManager = std::make_shared<AndroidServiceManager>(); auto app = std::make_shared<BluetoothApp>(scanner, connector, serviceManager); - 业务层仅调用接口方法,不包含任何平台特定代码。
- 单元测试时注入 Mock 实现,以验证业务逻辑而不依赖硬件。
2.5 调试与验证
| 测试点 | 方法 | 预期行为 |
|---|---|---|
| 接口方法签名验证 | 编译时检查所有派生类是否实现纯虚函数 | 编译通过无 override 遗漏 |
| 回调注入正确性 | 使用 MockScanner,StartScan 后立即调用回调 | 业务层收到设备信息,状态机推进 |
| 连接状态转换 | MockConnector 模拟连接成功/超时/断开 | 上层收到对应状态回调,不产生异常 |
| 服务发现与读写 | MockServiceManager 返回预设服务列表 | 业务层正确解析 GATT 属性 |
2.6 常见错误与避坑
❌ 常见错误:在接口中暴露平台类型(如
✅ 正确做法:使用纯数据传输对象(如
android::sp<BluetoothDevice>),导致注入实现必须携带 Android 头文件。✅ 正确做法:使用纯数据传输对象(如
DeviceAddress、ConnectionHandle)作为参数,与平台类型解耦。
💡 最佳实践:为每个接口提供一个空实现(Null Object Pattern),让业务层可以安全地在缺少某些功能时优雅降级,避免空指针检查污染代码。
2.7 性能与稳定性优化
- 接口粒度控制:避免在
IServiceManager中塞入连接断开功能,保持单一职责;过大的接口会导致实现类无法复用。 - 异步设计:所有耗时操作(扫描、连接、服务发现)应通过回调异步返回,接口方法本身不应阻塞调用线程。
- 连接句柄生命周期管理:
ConnectionHandle应在IConnector内部生成并回收,避免外部缓存失效。 - 回调线程策略:在接口文档中明确回调执行线程(如 UI 线程 / 后台线程),实现类需遵守约定以避免死锁。
- 内存控制:长连接场景下,
IScanner的扫描结果回调应支持批量化或节流,防止高频率回调导致业务层堆积。