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 对齐,应用运行期间始终复用同一对象。

下图描述了从接口定义到依赖注入的完整绑定链路:

BleManager 绑定流程(@Module / @Provides / @Singleton) BleManager (接口) BleManagerImpl (实现类) @Module BleModule (Hilt 模块) @Provides 方法返回 BleManager @Singleton 作用域 (单例) SingletonComponent (Hilt 生成容器) MainViewModel (@Inject constructor) 注意事项 • @Singleton 作用域必须与 @Module 所在 Component 匹配(默认 SingletonComponent) • @Provides 方法返回类型必须写 BleManager 而不是 BleManagerImpl • Application 类上需添加 @HiltAndroidApp 才能启用 Hilt

4.2 实现流程

  1. 定义接口:BleManager 声明必要方法,与具体实现解耦。
  2. 编写实现:BleManagerImpl 类实现接口,内部管理 BLE 扫描器、连接状态机。
  3. 创建 Hilt 模块:使用 @Module 注解类 BleModule,并标记 @InstallIn(SingletonComponent.class)。
  4. 提供绑定:在模块内添加 @Provides @Singleton 方法,返回类型为 BleManager,方法体内构建 BleManagerImpl 实例。
  5. 注入使用:目标类(如 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 管理异步任务。