第3章 依赖注入集成BLE的设计模式:桥接模式、工厂模式、单例管理
3.1 三种模式在BLE集成中的定位
在依赖注入框架中集成BLE组件,需要解决三个核心问题:抽象与实现的解耦、复杂对象的统一创建、全局资源的生命周期管控。桥接模式、工厂模式与单例管理分别对应上述三个问题,形成一套可复用的架构方案。
| 设计模式 | 核心作用 | 在BLE集成中的职责 |
|---|---|---|
| 桥接模式(Bridge) | 将抽象部分与实现部分分离 | 分离BLE操作接口(扫描、连接、收发)与底层平台实现(Android/iOS) |
| 工厂模式(Factory) | 封装对象创建逻辑 | 统一创建BLE适配器、设备代理、服务特征对象,避免直接new依赖 |
| 单例管理(Singleton) | 确保全局唯一实例 | 管理BLE中央管理器、应用上下文、连接池等共享资源 |
架构提示:三种模式并非孤立使用,而是通过依赖注入容器组合为有机整体。单例提供全局状态,工厂封装创建细节,桥接保证平台可移植性。
3.2 桥接模式:抽象与实现的分离
3.2.1 原理
桥接模式将BLE操作接口(扫描、连接、发现服务、收发数据)定义为抽象层,而具体实现委托给平台适配器。抽象层与实现层通过组合关系连接,两者可以独立演化。
3.2.2 接口定义
// 抽象接口:BLE 适配器
protocol IBLEAdapter {
fun startScan(timeout: Long): Flow<ScanResult>
fun connect(deviceId: String): Flow<ConnectionState>
fun discoverServices(deviceId: String): Flow<List<BLEService>>
fun writeCharacteristic(deviceId: String, service: String, char: String, data: ByteArray): Flow<WriteResult>
fun readCharacteristic(deviceId: String, service: String, char: String): Flow<ByteArray>
fun setNotification(deviceId: String, service: String, char: String, enable: Boolean): Flow<NotificationData>
fun disconnect(deviceId: String)
fun release()
}
3.2.3 实现示例
// Android 平台实现
class AndroidBLEAdapter @Inject constructor(
private val context: Context
) : IBLEAdapter {
private val bluetoothManager: BluetoothManager by lazy {
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
}
private val bluetoothAdapter: BluetoothAdapter by lazy {
bluetoothManager.adapter
}
override fun startScan(timeout: Long): Flow<ScanResult> = callbackFlow {
val scanner = bluetoothAdapter.bluetoothLeScanner
?: throw IllegalStateException("Bluetooth LE not supported")
// ... 扫描逻辑实现
}
override fun release() {
// 释放 Android 平台资源
}
}
// iOS 平台实现
class iOSBLEAdapter @Inject constructor(
private val centralManager: CBCentralManager
) : IBLEAdapter {
override fun startScan(timeout: Long): Flow<ScanResult> = callbackFlow {
// ... iOS CoreBluetooth 扫描实现
}
override fun release() {
// 释放 iOS 平台资源
}
}
桥接优势:当新增平台(如HarmonyOS、Windows)时,只需新增一个实现类,抽象接口和上层业务代码完全不受影响。依赖注入容器负责将正确的平台实现注入到消费者中。
3.3 工厂模式:统一对象创建
3.3.1 原理
BLE组件体系中存在大量复杂对象(适配器、代理、服务特征包装器),直接使用构造函数创建会导致依赖混乱。工厂模式集中管理这些对象的创建逻辑,并配合DI框架提供注入所需的实例。
3.3.2 工厂接口与实现
// 工厂抽象
interface BLEAdapterFactory {
fun createAdapter(): IBLEAdapter
fun createDeviceProxy(deviceId: String): BLEDeviceProxy
fun createServiceWrapper(service: BLEService): ServiceWrapper
}
// Android 平台工厂
class AndroidBLEAdapterFactory @Inject constructor(
private val context: Context
) : BLEAdapterFactory {
override fun createAdapter(): IBLEAdapter {
return AndroidBLEAdapter(context)
}
override fun createDeviceProxy(deviceId: String): BLEDeviceProxy {
val adapter = createAdapter()
return BLEDeviceProxy(adapter, deviceId)
}
override fun createServiceWrapper(service: BLEService): ServiceWrapper {
return ServiceWrapper(service)
}
}
// iOS 平台工厂
class iOSBLEAdapterFactory @Inject constructor(
private val centralManager: CBCentralManager
) : BLEAdapterFactory {
override fun createAdapter(): IBLEAdapter {
return iOSBLEAdapter(centralManager)
}
override fun createDeviceProxy(deviceId: String): BLEDeviceProxy {
val adapter = createAdapter()
return BLEDeviceProxy(adapter, deviceId)
}
override fun createServiceWrapper(service: BLEService): ServiceWrapper {
return ServiceWrapper(service)
}
}
3.3.3 在DI模块中注册工厂
// Hilt 模块示例
@Module
@InstallIn(SingletonComponent::class)
object BLEFactoryModule {
@Provides
@Singleton
fun provideAdapterFactory(context: Context): BLEAdapterFactory {
return if (Platform.isAndroid()) {
AndroidBLEAdapterFactory(context)
} else {
// 通过依赖注入获取 iOS 的 CBCentralManager
iOSBLEAdapterFactory(provideCentralManager())
}
}
@Provides
fun provideAdapter(factory: BLEAdapterFactory): IBLEAdapter {
return factory.createAdapter()
}
}
工厂模式陷阱:避免在工厂内部持有长时间存活的状态(如缓存列表),否则会导致单例与工厂职责混淆。工厂只负责“创建”,不负责“管理生命周期”。生命周期管理应交给单例或DI容器。
3.4 单例管理:全局资源管控
3.4.1 原理
BLE系统中存在多个必须全局唯一的资源:蓝牙中央管理器(CBCentralManager / BluetoothAdapter)、应用上下文、已连接设备池、扫描回调注册表等。单例模式确保这些资源不会重复创建,避免状态冲突。
3.4.2 BLEManager单例设计
@Singleton
class BLEManager @Inject constructor(
private val adapterFactory: BLEAdapterFactory,
private val adapter: IBLEAdapter
) {
// 设备连接池:全局唯一
private val devicePool = ConcurrentHashMap<String, BLEDeviceProxy>()
// 扫描状态
private val _scanState = MutableStateFlow<ScanState>(ScanState.Idle)
val scanState: StateFlow<ScanState> get() = _scanState.asStateFlow()
// 连接状态聚合
private val _connectionStates = MutableStateFlow<Map<String, ConnectionState>>(emptyMap())
val connectionStates: StateFlow<Map<String, ConnectionState>> get() = _connectionStates.asStateFlow()
fun startScan(timeout: Long = 10_000) {
if (_scanState.value is ScanState.Scanning) return
_scanState.value = ScanState.Scanning
adapter.startScan(timeout)
.onEach { result ->
// 处理扫描结果
}
.catch { error ->
_scanState.value = ScanState.Error(error.message ?: "Unknown error")
}
.launchIn(CoroutineScope(Dispatchers.IO))
}
suspend fun connect(deviceId: String): ConnectionState {
return adapter.connect(deviceId)
.map { state ->
if (state is ConnectionState.Connected) {
devicePool[deviceId] = adapterFactory.createDeviceProxy(deviceId)
}
state
}
.first()
}
fun getDeviceProxy(deviceId: String): BLEDeviceProxy? {
return devicePool[deviceId]
}
fun release() {
devicePool.values.forEach { it.disconnect() }
devicePool.clear()
adapter.release()
}
}
3.4.3 单例的依赖注入注册
// Hilt 单例绑定
@Module
@InstallIn(SingletonComponent::class)
object BLEManagerModule {
@Provides
@Singleton
fun provideBLECentralManager(context: Context): CBCentralManager {
// 仅iOS需要,Android通过context获取
return CBCentralManager(context, null, null)
}
@Provides
@Singleton
fun provideBLEManager(
adapterFactory: BLEAdapterFactory,
adapter: IBLEAdapter
): BLEManager {
return BLEManager(adapterFactory, adapter)
}
}
单例边界:BLEManager 单例应仅管理跨组件共享的状态(连接池、扫描状态),具体的业务数据(设备名称、服务列表)应通过数据层单独管理,避免单例膨胀为“上帝对象”。
3.5 三种模式的协作流程
在依赖注入框架下,三种模式按以下流程协同工作:
- DI容器启动 → 通过
@Singleton创建BLEManager实例(单例管理)。 - 消费方请求依赖 → DI容器查找
BLEAdapterFactory工厂实例(工厂模式)。 - 工厂创建适配器 → 根据平台类型创建
AndroidBLEAdapter或iOSBLEAdapter(桥接模式)。 - 适配器注入消费者 → ViewModel/Service 通过构造函数接收
IBLEAdapter抽象,调用扫描、连接等方法。 - 单例协调全局状态 →
BLEManager统一管理连接池和扫描状态,避免多个消费者之间的资源冲突。
3.6 常见错误与优化
| 错误/问题 | 原因 | 解决方案 |
|---|---|---|
| 工厂内缓存对象导致内存泄漏 | 工厂持有已创建的组件引用,阻止GC回收 | 工厂只负责创建,不维护缓存;缓存由单例管理 |
| 桥接接口粒度过粗 | 单个接口包含扫描+连接+数据收发,导致平台实现耦合 | 按职责拆分接口:IScanner、IConnector、IDataChannel |
| 单例持有Activity Context | 单例长生命周期持有短生命周期的Context | 使用Application Context注入,避免内存泄漏 |
| 多个单例相互循环依赖 | BLEManager依赖Factory,Factory依赖Manager | 引入中间接口或通过DI容器延迟注入(Lazy) |
| 平台条件判断分散 | 多处使用 if(platform) 判断Android/iOS | 统一在工厂模块内做平台分发,其余代码面向接口编程 |
3.7 性能与稳定性建议
- 桥接层使用协程Flow:所有BLE操作返回
Flow<T>,方便上层做响应式组合和生命周期取消,避免回调嵌套。 - 工厂实例池化:对于频繁创建的轻量对象(如ServiceWrapper),工厂可配合对象池复用,减少GC压力。
- 单例状态线程安全:使用
ConcurrentHashMap、Mutex或StateFlow保证多线程环境下的状态一致性。 - DI作用域收敛:BLE相关的单例统一放在
@Singleton作用域,避免跨作用域导致的重复实例。 - 平台适配器懒加载:在首次需要执行BLE操作时才初始化平台适配器,避免冷启动时加载不必要的蓝牙服务。
架构总结:桥接模式提供了平台无关的抽象层,工厂模式封装了复杂对象的创建过程,单例管理保证了全局资源的唯一性与一致性。三者通过依赖注入容器无缝集成,形成一套高内聚、低耦合的BLE组件架构,可稳定支撑多平台蓝牙开发需求。