9.1 Jetpack Hilt 使用简介

295 阅读3分钟

Hilt 依赖注入

由于依赖倒置原则,应该依赖抽象而不依赖具体实现。实际项目中代码中一个类的功能总是依赖许多其他类来实现,项目越来越大,各种依赖也越来越多,这个时候就需要使用依赖注入来进行统一管理。

依赖注入主要负责两个方面:

  1. 创建容器统一管理依赖的创建
  2. 将容器中的依赖注入到需要的地方

image.png

使用依赖注入框架的好处在于只需要关创建容器创建依赖,注入的模板代码由框架自动生成。

1 Hilt 中创建容器

使用 Hilt 中的注解,编译时框架会自动为其生成对应的容器。

@HiltAndroidApp

集成 Hilt 的应用必须在 Application 加上 @HiltAndroidApp 注解。

@HiltAndroidApp
class ExampleApplication : Application() { ... }

@HiltViewModel

在 ViewModel 上使用

@HiltViewModel
class ExampleVm : ViewModel() { ... }

@AndroidEntryPoint

在 Activity、Fragment、View、Service、BroadcastReceiver 这些类上使用。

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

容器的生命周期

Hilt 中容器是以 Component 形式存在,Hilt 会根据容器对应的 Android 类的生命周期自动创建/销毁这些 Component。

image.png

ActivityRetainedComponent 不会在配置变更导致的 Activity#onDestory() 方法中销毁。

image.png

通过不同的容器将具体的依赖注入的实现细化拆分,Hilt 会为每个容器注解标记的类生成单独的容器。

容器间依据生命周期的长短还存在关联关系,上级容器中的依赖可以在下级容器中使用。声明依赖时配合 Scope 注解来实现依赖的复用。

2 Hilt 中创建依赖

@Inject

将 @Inject 注解标记在类的构造函数上,Hilt 会根据构造函数来生成这个类的实例,作为依赖放入容器中。

此外 @Inject 还可以标记在属性上,此时注解的作用是告知 Hilt 这个属性需要容器为其提供具体的依赖。

class AnalyticsAdapter @Inject constructor(
  private val service: AnalyticsService
) { ... }


@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapte
  ...
}

@Module

使用 @Module 注解标记一个类,在配合 @Binds 或 @Providers 注解标记的方法来创建无法使用 @Inject 注解类构造函数的依赖。

@Binds

当依赖是接口类型时使用。

  • 方法的返回类型是依赖的类型
  • 方法的参数是依赖的实现类型
interface AnalyticsService {
  fun analyticsMethods()
}

class AnalyticsServiceImpl @Inject constructor(
  ...
) : AnalyticsService { ... }

@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {

  @Binds
  abstract fun bindAnalyticsService(
    analyticsServiceImpl: AnalyticsServiceImpl
  ): AnalyticsServic
}

@Provides

当依赖是第三方库中的类时使用

@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {

  @Provides
  fun provideAnalyticsService(
    // Potential dependencies of this type
  ): AnalyticsService {
      return Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
              .create(AnalyticsService::class.java)
  }
}

@Provides 和 @Binds 不能同时存在于一个 @Module 标记 的类中。

@Qualifier

同一个类型不同实现的依赖需要先使用 @Qualifier 注解定义不同的限定符,再在不同的实现上用限定符加以区分。

@Qualifier
annotation class DebugURL
@Qualifier
annotation class PreProductURL
@Qualifier
annotation class ProductURL

@Module
@InstallIn(SingletonComponent::class)
object NetServiceModule {

    @Provides
    @DebugURL
    fun providerDebugBaseUrl():String = WanAndroidService.baseUrlDebug

    @Provides
    @PreProductURL
    fun providerPreProductBaseUrl():String = WanAndroidService.baseUrlPreProduct

    @Provides
    @ProductURL
    fun providerProductBaseUrl():String = WanAndroidService.baseUrlProduct


    @Provides
    @Singleton
    fun providerWanAndroidService(@DebugURL baseUrl:String):WanAndroidService = RetrofitClient.apply {
        init(baseUrl)
    }.createService(WanAndroidService::class.java)
}

Scope 和 @InstallIn

  • Scope 注解:将依赖添加到与 Scope 对应的容器中
  • @InstallIn 将 @Module 提供的依赖添加到指定的容器中,且 @Binds 或 @Provides 方法声明的 Scope 注解需要跟 @InstallIn 指定的容器对应

默认情况下这些依赖是 unscoped 的, 每当需要注入时都会生成一个新的实例。 使用 Scope 和 InstallIn 注解为依赖指定容器,在指定的容器或其下级容器中依赖只会创建一次。

image.png

Scope 和 Component 的对关系:

image.png

总结

  1. @HiltAndroidApp 注解 Application 创建 App 容器
  2. @HiltViewModel 注解 ViewModel 、 @AndroidEntryPoint 注解 Android 组件 创建下级容器
  3. 自己写的类用 @Inject 注解构造函数创建依赖
  4. 接口使用 @Module 和 @Binds 创建依赖
  5. 第三方类使用 @Module 和 @Provides 创建依赖
  6. 相同类型的不同实例依赖用 @Qualifier 区分
  7. 使用 Scope 和 @InstallIn 将依赖配置到指定容器实现复用