Hilt 依赖注入
由于依赖倒置原则,应该依赖抽象而不依赖具体实现。实际项目中代码中一个类的功能总是依赖许多其他类来实现,项目越来越大,各种依赖也越来越多,这个时候就需要使用依赖注入来进行统一管理。
依赖注入主要负责两个方面:
- 创建容器统一管理依赖的创建
- 将容器中的依赖注入到需要的地方
使用依赖注入框架的好处在于只需要关创建容器与创建依赖,注入的模板代码由框架自动生成。
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。
ActivityRetainedComponent 不会在配置变更导致的 Activity#onDestory() 方法中销毁。
通过不同的容器将具体的依赖注入的实现细化拆分,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 注解为依赖指定容器,在指定的容器或其下级容器中依赖只会创建一次。
Scope 和 Component 的对关系:
总结
- @HiltAndroidApp 注解 Application 创建 App 容器
- @HiltViewModel 注解 ViewModel 、 @AndroidEntryPoint 注解 Android 组件 创建下级容器
- 自己写的类用 @Inject 注解构造函数创建依赖
- 接口使用 @Module 和 @Binds 创建依赖
- 第三方类使用 @Module 和 @Provides 创建依赖
- 相同类型的不同实例依赖用 @Qualifier 区分
- 使用 Scope 和 @InstallIn 将依赖配置到指定容器实现复用