第3章 Hilt基础模块与组件:@Module、@InstallIn、@Provides、@Binds、组件作用域

3.1 本章知识结构

Hilt 的依赖注入能力建立在模块化组件之上。本章围绕 5 个核心注解与组件作用域展开,涵盖从模块声明、实例提供到生命周期绑定的完整链路。

注解/组件职责使用位置
@Module标记类为 Hilt 依赖提供模块类级别
@InstallIn指定模块安装到哪个 Hilt 组件类级别(与 @Module 配合)
@Provides在模块内提供具体实例的方法模块内部方法
@Binds将接口抽象绑定到实现类模块内部抽象方法
组件作用域控制依赖实例的生命周期(如 @Singleton)类级别 + 模块方法

3.2 原理:Hilt 模块化依赖注入架构

Hilt 将依赖的提供逻辑集中到 @Module 中,每个模块通过 @InstallIn 声明自身属于哪个组件。组件(如 SingletonComponent、ActivityComponent)负责管理实例的生命周期,而 @Provides 和 @Binds 则分别解决“如何创建实例”与“如何绑定抽象”两个核心问题。组件作用域进一步将实例的存活范围与组件对齐,避免内存泄漏与状态不一致。

核心原则:模块只负责“如何提供”,组件决定“何时存活”,作用域保证“谁该共享”。三者分离,各司其职。

Hilt 模块化依赖注入架构 @Module @Provides 提供实例 @Binds 绑定抽象 @InstallIn(Component::class) Hilt 组件 SingletonComponent / ActivityComponent / ViewModelComponent 组件作用域 @Singleton / @ActivityScoped 模块声明 → 实例提供 → 安装到组件 → 作用域限定生命周期,构成完整注入链路

3.3 实现流程与代码示例

3.3.1 定义 Module 并使用 @Provides

当依赖是一个具体类(或第三方库对象)时,使用 @Provides 在模块中编写工厂方法。方法返回类型即注入目标所需类型。

// 定义一个网络客户端类(待注入)
class OkHttpClient @Inject constructor() {
    fun newCall(request: Request): Call { /* ... */ }
}

// 定义 Hilt 模块
@Module
@InstallIn(SingletonComponent::class)   // 安装到全局单例组件
object NetworkModule {

    @Provides
    @Singleton                              // 作用域:全局单例
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient()
    }
}

最佳实践:@Provides 方法名建议以 provide 开头,返回类型明确,方法体保持轻量。如果创建实例开销较大,配合 @Singleton 缓存结果。

3.3.2 使用 @Binds 绑定接口与实现

当依赖是一个接口,需要将抽象与具体实现解耦时,使用 @Binds。与 @Provides 不同,@Binds 必须声明为抽象方法,且参数是接口的实现类实例。

// 定义接口
interface ApiService {
    fun fetchData(): String
}

// 实现类
class ApiServiceImpl @Inject constructor() : ApiService {
    override fun fetchData(): String = "Data from API"
}

// 模块中使用 @Binds 绑定
@Module
@InstallIn(ActivityComponent::class)   // 安装到 Activity 组件
abstract class ApiModule {

    @Binds
    abstract fun bindApiService(impl: ApiServiceImpl): ApiService
}

注意:@Binds 方法必须是 abstract 的,且只能有一个参数。方法体不写任何逻辑,Hilt 自动生成实现代码。如果需要配置或创建参数,改用 @Provides。

3.3.3 @InstallIn 与组件选择

@InstallIn 决定了模块中的依赖可以被哪些组件使用,也间接控制了依赖的可见性与生命周期。常用组件对照表如下:

组件生命周期适用于
SingletonComponent整个 Application全局单例依赖(数据库、网络客户端)
ActivityComponentActivity 从创建到销毁与界面相关的依赖(适配器、屏幕管理器)
FragmentComponentFragment 从创建到销毁Fragment 内使用的临时依赖
ViewModelComponentViewModel 存活期间与 ViewModel 绑定的业务逻辑依赖

3.3.4 组件作用域注解

组件作用域确保同一个依赖在对应组件的生命周期内只创建一次。作用域注解与组件一一对应:

// @Singleton —— 对应 SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides
    @Singleton          // 全局唯一实例
    fun provideDatabase(): Database = AppDatabase()
}

// @ActivityScoped —— 对应 ActivityComponent
@Module
@InstallIn(ActivityComponent::class)
object ScreenModule {
    @Provides
    @ActivityScoped     // Activity 内唯一实例
    fun provideScreenTracker(): ScreenTracker = ScreenTracker()
}

// @ViewModelScoped —— 对应 ViewModelComponent
@Module
@InstallIn(ViewModelComponent::class)
object ViewModelModule {
    @Provides
    @ViewModelScoped    // ViewModel 内唯一实例
    fun provideUserState(): UserState = UserState()
}

3.4 调试与验证

Hilt 在编译期会生成 Dagger* 实现类,可以通过以下方式验证模块配置是否正确:

  • 在 build/generated/source/kapt/ 目录下查看生成的 Dagger*Component 类,确认模块方法是否被正确调用。
  • 在 AndroidManifest.xml 中确认已配置 @HiltAndroidApp,并且 Application 类已标注。
  • 运行时期望通过 @Inject 注入的依赖如果为 null,检查模块是否被安装到正确的组件。
// 典型调试日志(成功注入时无异常)
// 若模块缺失或作用域不匹配,编译期报错:
// error: [Dagger/MissingBinding] com.example.ApiService cannot be provided without an @Provides or @Binds method

3.5 常见错误与避坑指南

错误场景原因解决方案
编译报错 MissingBinding忘记添加 @Provides 或 @Binds检查模块内是否覆盖所有注入类型
@Binds 方法有方法体@Binds 必须是抽象方法去掉方法体,保留 abstract 声明
作用域不匹配@Singleton 用在 ActivityComponent 模块中确保作用域与 @InstallIn 的组件一致
模块未安装到任何组件遗漏 @InstallIn 注解为模块添加 @InstallIn 并指定目标组件
多个模块提供同类型产生冲突绑定使用 @Named 或 @Qualifier 区分

3.6 性能与稳定性优化

  • 作用域粒度控制:避免滥用 @Singleton,将全局依赖控制在最小集合(网络客户端、数据库、配置管理器),减少内存常驻。
  • @Provides 方法轻量化:不要在 provide 方法中执行耗时操作,若必须初始化,使用 @Singleton 缓存结果或借助 Lazy 注入。
  • 模块组织:按功能或层(数据层、业务层、UI层)拆分模块,避免单个模块包含过多跨领域依赖,提升编译速度与可维护性。
  • 编译期检查:开启 Hilt 的 strictBinding 校验(在 build.gradle 中配置 kapt { arguments { arg("dagger.hilt.strictBinding", "true") } }),提前捕获未使用的绑定。

本章小结:@Module 声明依赖提供单元,@InstallIn 将模块安装到指定组件,@Provides 负责实例创建,@Binds 负责接口绑定,组件作用域则控制实例的生命周期。掌握这 5 个元素,就掌握了 Hilt 依赖注入的模块化核心。下一章将深入 ViewModel 的注入机制,将本章的模块能力应用到 MVVM 架构中。