Android Hilt 依赖注入 使用详解

537 阅读3分钟

Hilt 是 Google 基于 Dagger 开发的 Android 专属依赖注入框架,大幅简化了 Dagger 的配置复杂度。以下是核心要点:


一、基础配置

  1. 项目级 build.gradle:

    dependencies {
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.48.1'
    }
    
  2. 模块级 build.gradle:

    plugins {
        id 'kotlin-kapt'
        id 'com.google.dagger.hilt.android'
    }
    
    dependencies {
        implementation 'com.google.dagger:hilt-android:2.48.1'
        kapt 'com.google.dagger:hilt-compiler:2.48.1'
    }
    
  3. 初始化 Application:

    @HiltAndroidApp
    class MyApp : Application()  // 必须添加此注解
    

二、核心注解及功能

注解作用场景说明
@HiltAndroidAppApplication 类触发 Hilt 代码生成,生成顶层依赖容器 SingletonComponent
@AndroidEntryPointAndroid 组件(Activity/Fragment/Service/View)使该组件支持依赖注入,必须添加到支持组件的类上
@Inject构造函数/字段标记需要注入的依赖(构造函数)或声明需要注入的字段(属性注入)
@Module依赖提供模块类定义提供依赖的模块
@InstallInModule 类指定模块的作用域(如 SingletonComponent::class
@ProvidesModule 内部方法提供实例(适用于第三方库/无法修改构造的类)
@BindsModule 内部抽象方法绑定接口到实现类(更高效,需在抽象类/接口中使用)
@Singleton@Provides/@Binds方法声明单例依赖(作用域为 SingletonComponent
@HiltViewModelViewModel 类提供 ViewModel 依赖注入
@Qualifier自定义注解解决同一类型多实例冲突(需自定义注解)

三、使用场景与代码示例

  1. 构造函数注入(推荐):

    class UserRepository @Inject constructor(
        private val apiService: ApiService
    ) { ... }
    
  2. 字段注入(Android 组件):

    @AndroidEntryPoint
    class MainActivity : AppCompatActivity() {
        @Inject lateinit var userRepo: UserRepository
    }
    
  3. 提供接口实现@Binds):

    @Module
    @InstallIn(SingletonComponent::class)
    interface AuthModule {
        @Binds
        fun bindAuthService(impl: AuthServiceImpl): AuthService
    }
    
  4. 提供第三方库实例@Provides):

    @Module
    @InstallIn(SingletonComponent::class)
    object NetworkModule {
        @Singleton
        @Provides
        fun provideRetrofit(): Retrofit = Retrofit.Builder().baseUrl("...").build()
    }
    
  5. ViewModel 注入

    @HiltViewModel
    class ProfileViewModel @Inject constructor(
        private val userRepo: UserRepository
    ) : ViewModel() { ... }
    
    // Activity/Fragment 中使用
    private val viewModel: ProfileViewModel by viewModels()
    

四、作用域与组件生命周期

Hilt 通过组件层级管理作用域,每个组件对应特定生命周期:

组件作用域注解生命周期
SingletonComponent@SingletonApplication 生命周期
ActivityRetainedComponent@ActivityRetainedScoped配置变更后仍存活(ViewModel 层级)
ActivityComponent@ActivityScopedActivity 生命周期
FragmentComponent@FragmentScopedFragment 生命周期
ViewComponent@ViewScopedView 生命周期
ViewModelComponent@ViewModelScopedViewModel 生命周期

规则

  • 依赖的作用域必须 ≤ 组件的生命周期(例:@ActivityScoped 依赖不能用在 @Singleton 模块中)
  • 子组件可访问父组件的依赖,反之不行

五、复杂场景解决方案

  1. 同一类型多实例(使用 @Qualifier):

    // 定义限定符
    @Qualifier
    @Retention(AnnotationRetention.RUNTIME)
    annotation class AuthInterceptorClient
    
    @Qualifier
    @Retention(AnnotationRetention.RUNTIME)
    annotation class LoggingInterceptorClient
    
    // 提供不同实例
    @Module
    @InstallIn(SingletonComponent::class)
    object NetworkModule {
        @AuthInterceptorClient
        @Provides
        fun provideAuthClient(): OkHttpClient { ... }
    
        @LoggingInterceptorClient
        @Provides
        fun provideLoggingClient(): OkHttpClient { ... }
    }
    
    // 使用处指定
    class UserRepo @Inject constructor(
        @AuthInterceptorClient private val client: OkHttpClient
    )
    
  2. 动态参数依赖(使用 @AssistedInject):

    // 添加依赖:implementation "com.squareup.inject:assisted-inject:0.8.1"
    class ImageLoader @AssistedInject constructor(
        private val networkService: NetworkService,
        @Assisted private val url: String  // 动态参数
    ) {
        @AssistedFactory
        interface Factory {
            fun create(url: String): ImageLoader
        }
    }
    
    // 使用
    @AndroidEntryPoint
    class MyActivity : AppCompatActivity() {
        @Inject lateinit var imageLoaderFactory: ImageLoader.Factory
    
        fun loadImage() {
            val loader = imageLoaderFactory.create("https://example.com/img.png")
        }
    }
    
  3. 非标准类获取依赖(使用 EntryPoint):

    @EntryPoint
    @InstallIn(SingletonComponent::class)
    interface MyEntryPoint {
        fun getDependency(): MyDependency
    }
    
    // 在非注入类中使用
    val entryPoint = EntryPointAccessors.fromApplication(context, MyEntryPoint::class.java)
    val dependency = entryPoint.getDependency()
    

六、关键注解对比表

注解组合适用场景主要区别
@Provides vs @Binds提供依赖实现@Binds 用于抽象方法(效率更高)
@Singleton vs @ActivityScoped作用域控制生命周期范围不同
@Inject vs @Provides依赖提供方式@Inject 优先用于构造函数
@Qualifier vs @Named解决多实例冲突@Qualifier 是自定义注解的标准方式