4. Hilt模块绑定BleManager实现
4.1 原理:接口分离与作用域管理
在模块化架构中,BleManager 应作为抽象接口定义蓝牙扫描、连接、数据收发契约,而具体实现 BleManagerImpl 内聚 BLE 协议栈细节。Hilt 通过 @Module + @Provides 模式将实现绑定到接口,并借助 @Singleton 作用域确保整个应用中只存在一个 BleManager 实例,避免重复创建连接或资源泄漏。
核心约定:
BleManager接口 — 定义公开 API(scan、connect、disconnect、write)。BleManagerImpl实现 — 持有 BluetoothAdapter、Handler、连接池等生命周期敏感资源。@Module注解标记的 Hilt 模块 — 告诉 Hilt 如何提供依赖。@Provides方法 — 在模块中返回接口类型的实例,方法内部构建BleManagerImpl。@Singleton作用域 — 将实例生命周期与 Application 对齐,应用运行期间始终复用同一对象。
下图描述了从接口定义到依赖注入的完整绑定链路:
4.2 实现流程
- 定义接口:
BleManager声明必要方法,与具体实现解耦。 - 编写实现:
BleManagerImpl类实现接口,内部管理 BLE 扫描器、连接状态机。 - 创建 Hilt 模块:使用
@Module注解类BleModule,并标记@InstallIn(SingletonComponent.class)。 - 提供绑定:在模块内添加
@Provides @Singleton方法,返回类型为BleManager,方法体内构建BleManagerImpl实例。 - 注入使用:目标类(如 ViewModel)通过
@Inject构造器或字段注入BleManager。
4.3 关键代码与 API
4.3.1 接口定义
package com.example.bleapp.domain;
public interface BleManager {
void startScan();
void stopScan();
boolean connect(String deviceAddress);
void disconnect();
void writeCharacteristic(String serviceUuid, String charUuid, byte[] data);
}
4.3.2 实现类(简化)
package com.example.bleapp.data;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import com.example.bleapp.domain.BleManager;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class BleManagerImpl implements BleManager {
private final Context appContext;
private BluetoothAdapter bluetoothAdapter;
@Inject
public BleManagerImpl(Context appContext) {
this.appContext = appContext.getApplicationContext();
this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
@Override
public void startScan() { /* 扫描逻辑 */ }
@Override
public void stopScan() { /* 停止扫描 */ }
@Override
public boolean connect(String deviceAddress) { /* 连接逻辑 */ return true; }
@Override
public void disconnect() { /* 断开连接 */ }
@Override
public void writeCharacteristic(String serviceUuid, String charUuid, byte[] data) { /* 写入数据 */ }
}
4.3.3 Hilt 模块(BleModule)
package com.example.bleapp.di;
import com.example.bleapp.data.BleManagerImpl;
import com.example.bleapp.domain.BleManager;
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.components.SingletonComponent;
import javax.inject.Singleton;
@Module
@InstallIn(SingletonComponent.class)
public class BleModule {
@Provides
@Singleton
public static BleManager provideBleManager(BleManagerImpl impl) {
// 直接返回已由 Hilt 通过 @Inject 构造器创建的实现类
return impl;
}
}
替法:若希望模块自行创建实例,可在方法内写
return new BleManagerImpl(context);,但更推荐依赖 Hilt 自动注入 BleManagerImpl 的构造函数,以免手动管理 Context。
4.3.4 在 ViewModel 中注入使用
package com.example.bleapp.presentation;
import androidx.lifecycle.ViewModel;
import com.example.bleapp.domain.BleManager;
import dagger.hilt.android.lifecycle.HiltViewModel;
import javax.inject.Inject;
@HiltViewModel
public class BleViewModel extends ViewModel {
private final BleManager bleManager;
@Inject
public BleViewModel(BleManager bleManager) {
this.bleManager = bleManager;
}
public void startScanning() {
bleManager.startScan();
}
}
4.4 调试与验证
| 验证项 | 操作方法 | 预期结果 |
|---|---|---|
| Hilt 组件生成 | 构建项目,查看 build/generated/source/hilt/ 下生成的 BleModule_ProvideBleManagerFactory |
工厂类正确调用 provideBleManager |
| 单例性验证 | 在两个不同 ViewModel 中注入 BleManager,在构造函数或 @Provides 方法中打印哈希码 |
两次哈希码相同,为同一实例 |
| 作用域冲突检查 | 检查 BleManagerImpl 是否也标注了 @Singleton(建议加上,保持一致性) |
无编译错误,Hilt 不会重复绑定 |
4.5 常见错误与避坑
- 未加
@Module或@InstallIn:Hilt 无法发现绑定,编译时出现 “Dagger does not know how to provide” 错误。 @Provides方法返回了具体类:错误示例:@Provides BleManagerImpl provideBleManager()。应使用BleManager接口类型。- 作用域不匹配:
@Singleton要求模块安装在SingletonComponent,若误用@InstallIn(ActivityComponent.class)会导致作用域冲突或编译警告。 - 未在 Application 类加
@HiltAndroidApp:所有 Hilt 功能均失效,注入会抛出RuntimeException。 - 手动
new BleManagerImpl()导致单例失效:应始终通过 Hilt 注入获取实例,禁止在业务代码中用new创建。
4.6 性能与稳定性优化
- 生命周期对齐:@Singleton 将 BleManager 的生命周期绑定到 Application,避免与 Activity/Fragment 耦合,防止因屏幕旋转重建连接。
- 资源初始化时机:在
BleManagerImpl的 @Inject 构造器中使用appContext.getApplicationContext(),杜绝 Activity Context 泄漏。 - 线程安全:若 BLE 操作涉及多线程,考虑在 @Singleton 的 BleManager 内部使用
synchronized或HandlerThread,确保连接状态一致。 - 懒加载:若不想在应用启动时即刻创建 BleManager,可改用
@Lazy注入,或通过@BindsInstance延迟初始化(高阶用法)。
警告:不要因为方便而在模块中使用
new Thread() 或 AsyncTask 初始化 BLE 扫描器;应让 Hilt 的 @Singleton 管理实例,BleManagerImpl 内部使用协程或 RxJava 管理异步任务。