前言
本blog 是学习Hilt官方文档后,随手记的笔记,欢迎一起探讨交流Hilt用法
Hilt 用到的注解和类
| 注解 | 解释 |
|---|---|
| @HiltAndroidApp | @HiltAndroidApp 会触发 Hilt 的代码生成操作,生成的代码包括应用的一个基类,该基类充当应用级依赖项容器 |
| @EntryPoint | “hilt管理代码与非hilt管理代码之间的边界它是代码首次进入 Hilt 所管理对象的图的位置” |
| @InstallIn | 指定要在其中安装入口点的组件 |
| @AndroidEntryPoint | 充当组件持有者 |
| EntryPointAccessors | 访问入口点工具 |
| @ApplicationContext | 预定义限定符 |
| @ActivityContext | 预定义限定符 |
| DFM (动态功能模块) | |
| @Inject | 提供依赖的对象 |
| @WorkerInject | WorkManager注入 |
| @Assisted | WorkManager注入 |
| @Singleton | 限定作用域 |
解决问题:
1.不能通过构造函数注入接口
2.不能通过构造函数注入不归您所有的类型(外部库的类)
解决思路:
@Module注释的Hilt模块类里面的@Binds注释的函数会告知 Hilt 如何提供某些类型的实例
示例:
@Module
@InstallIn(ActivityComponent::class) 依赖项目注入所有拓展自ActivityComponent的子类
abstract class AnalyticsModule {
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
说明:在 Hilt 模块内创建一个带有 @Binds 注释的抽象函数(@Binds 注释会告知 Hilt 在需要提供接口的实例时要使用哪种实现),
带有@Binds的函数会向 Hilt 提供以下信息:函数返回类型会告知 Hilt 函数提供哪个接口的实例。
函数参数会告知 Hilt 要提供哪种实现。
解决问题:
1.Retrofit、OkHttpClient、Room或者必须使用构建器模式创建实例,也同样无法使用构造器函数注入
解决思路:
@Provides注释函数会向 Hilt 提供以下信息
示例:
@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)
}
}
解决问题:
1.以依赖项的形式提供同一类型的不同实现
解决思路:
您可以使用限定符为同一类型定义多个绑定
限定符是一种注释,当为某个类型定义了多个绑定时,您可以使用它来标识该类型的特定绑定。
示例步骤:
1.定义要由于为@Binds 或 @Provides 方法添加注释的限定符
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient
@Module
@InstallIn(ApplicationComponent::class)
object NetworkModule {
@AuthInterceptorOkHttpClient
@Provides
fun provideAuthInterceptorOkHttpClient(
authInterceptor: AuthInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.build()
}
@OtherInterceptorOkHttpClient
@Provides
fun provideOtherInterceptorOkHttpClient(
otherInterceptor: OtherInterceptor
): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(otherInterceptor)
.build()
}
}
调用示例:
系统类中,自定义类中,hiltModule中
// As a dependency of another class.
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Provides
fun provideAnalyticsService(
@AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
): AnalyticsService {
return Retrofit.Builder()
.baseUrl("https://example.com")
.client(okHttpClient)
.build()
.create(AnalyticsService::class.java)
}
}
// As a dependency of a constructor-injected class.
class ExampleServiceImpl @Inject constructor(
@AuthInterceptorOkHttpClient private val okHttpClient: OkHttpClient
) : ...
// At field injection.
@AndroidEntryPoint
class ExampleActivity: AppCompatActivity() {
@AuthInterceptorOkHttpClient
@Inject lateinit var okHttpClient: OkHttpClient
}
最佳做法:
如果对某一个类型添加限定符@Qualifier,应向提供该依赖项的所有可能的方式添加限定符。避免遗漏造成注入错误的依赖项。
@Inject 告知注入位置,@ActivityContext 预定义限定符,系统默认提供
java注解可应用于形参(PARAMETER,)和local_paramter 本地变量
@InstallIn 可以关联的组件
| ApplicationComponent | Application |
| ActivityRetainedComponent | ViewModel |
| ActivityComponent | Activity |
| FragmentComponent | Fragment |
| ViewComponent | View |
| ViewWithFragmentComponent | 带有 @WithFragmentBindings 注释的 View |
| ServiceComponent | Service |
示例:
@Module
@InstallIn(xxxComponent::class)
class xxxModule{
@Binds
或
@Provides
}
因为 Hilt 直接从 ApplicationComponent 注入广播接收器.
解决问题:
解决思路:
前两个需要着重查看下,hilt提供的作用域。
| Application | ApplicationComponent | @Singleton |
| View Model | ActivityRetainedComponent | @ActivityRetainedScope |
| Activity | ActivityComponent | @ActivityScoped |
| Fragment | FragmentComponent | @FragmentScoped |
| View | ViewComponent | @ViewScoped |
| Service | ServiceComponent | @ServiceScoped |
hilt支持注入的类,Android类,四大组件viewModel,View
上面介绍在构造函数中不能注入的对象或者其他不属于应用的第三方类图的对象,通过build来构建。下面介绍在Hilt不支持的类中注入依赖项
对于不支持的类型,定义入口点绑定组件形式,然后通过类检索组件,获取组件中的类型
比如:ContentProvider 中要使用Hilt提供的依赖项
解决问题:
解决思路:访问入口点
示例
class ExampleContentProvider : ContentProvider() {
@EntryPoint
@InstallIn(ApplicationComponent::class)
interface ExampleContentProviderEntryPoint {
fun analyticsService(): AnalyticsService
}
...
}
DFM 如何设置依赖,通过在主模块中设置入口点(与非动态模块设置依赖方向相反),设置Dagger模块依赖主模块功能点 放入功能模块,功能模块使用drgger来构建。
优点:
- 依赖注入对于创建可扩展且可测试的Android应用是一项合适的技术
- 将容器作为在应用内不同部分共享各个类的实例的一种方式,以及使用工厂创建各个类实例的集中位置
- 手动依赖项注入要求您手动构造每个类及其依赖项,并借助容器和工厂重复使用和管理依赖项。
缺点:
- 应用变大,流程变多,不同的界面流程需要维护不同的容器创建销毁,工厂方法的维护。
- 将会编写大量的样板代码,很容易出错,生命周期管理操作不当会产生微小错误和内存泄露
手动注入需要写模板代码,多模块依赖顶层模块构建对象,需要依赖到不同子模块。 依赖管理,依赖范围管理,依赖生命周期管理,依赖复用。需要考虑
问题
hilt基于Dagger提供一套标准依赖注入做法,Hilt优化了Dagger依赖注入的功能,减少了样版代码的编写,依赖注入主要有两种方式,构造函数注入是最常用的,而参数注入使用优先级低于构造函数注入,对于接口,三方工具库等不能修改源码的依赖注入,需要创建Hilt模块,并使用@Module @Installin(依赖安装的类::class),@Provider(sdk的Builder构造),@Binds(接口,抽象类)注释解决依赖传递问题。
hilt用法xmind 脑图
