官网介绍已经讲解的非常详细了,这里就不做过多的介绍,只是记录下核心的思想和步骤,让应用可以跑起来,并且用到核心的依赖注入功能。(建议不要使用Hilt,去用Koin,Koin比这个Hilt好用太多了,简洁明了。参考:Kotlin替换Dagger2/Hilt的依赖注入框架–Koin)
浅谈依赖注入
依赖注入是控制反转的一种实现方式,目的是:
分
离
创
建
这
个
类
的
代
码
和
使
用
这
个
类
的
代
码
分离创建这个类的代码和使用这个类的代码
分离创建这个类的代码和使用这个类的代码。
为什么要分离呢?因为:一个类有特别多的依赖时,创建依赖的时候,又会导致其他更多的依赖被创建。这个时候如果不将这些创建代码分离开来,会导致创建逻辑异常复杂、代码极度耦合、测试难度加大等问题。
使用步骤
添加依赖项
如何添加依赖库,参考官方的添加依赖项,这里就不花篇幅讲解了。更全面的设置和最新的版本可以参考Dagger官网的指导。
必要注解
所有使用 Hilt 的应用都必须包含一个带有 @HiltAndroidApp 注释的 Application 类。
@HiltAndroidApp
class HiltApp : Application()
注入方式
以hilt应用代码为例,给Activity注入相应依赖:
// 依赖项
data class SimpleData constructor (var name: String)
data class EmunData constructor(val name: String)
interface InterfaceData {
fun getName(): String
}
class InterfaceDataImpl @Inject constructor() : InterfaceData {
override fun getName(): String {
return toString()
}
}
// 需要被注入依赖项的Android类
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var simpleData: SimpleData
@Inject lateinit var interfaceData: InterfaceData
@EmunB
@Inject lateinit var emunData: EmunData
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val tv = findViewById<TextView>(R.id.tvHint)
tv.textSize = 20F
tv.text = simpleData.name +
"\n${interfaceData.getName()}" +
"\n$this" +
"\n${emunData.name}"
}
}
数据和Android类(点我知道什么是Android类)都已经准备好了,那么我们开始注入数据。
- 注入 simpleData:
可以看到 simpleData 是通过 @Inject 注解来修饰的,这代表它是需要被注入的数据,那么怎么注入呢?注入代码如下:
注解:@Module @InstallIn(ActivityComponent::class) object SimpleDataModule { @Provides fun provideSimpleData(): SimpleData { return SimpleData("Name") } }- @Module:用于标记它是一个用于提供注入的模块类
- @InstallIn:用于标识这个 @Module 要安装在哪个Android类中(注意 @Module 和 @InstallIn 注解要一起使用)
- @Provides:用于注入,这个注解会被Hilt自动识别,并为所有的带有 @Inject 的 SimpleData 进行注入
- 注入interfaceData:这个数据是一个接口,不过注入注解也是 @Inject。注入代码如下:
可以看到这个是抽象类,可见是 Hilt 给它进行了实现。Hilt 如何找到这个 bindInterfaceData 方法所需要的参数呢?就是如下的 @Inject 注解告诉了 Hilt 如何给它提供参数。@Module @InstallIn(ActivityComponent::class) abstract class InterfaceDataModule { @Binds abstract fun bindInterfaceData(interfaceData: InterfaceDataImpl): InterfaceData }
注解:class InterfaceDataImpl @Inject constructor() : InterfaceData { override fun getName(): String { return toString() } }- @Binds:绑定一个类型到另一个类型的注入方式。这个注解只能修饰抽象方法,且有一个参数和一个返回值对应。上面的例子就是 InterfaceDataImpl 到 InterfaceData 的绑定注入。
- 注入 emunData:可以看到这属性多了一个注解 @EmunA,这个是用来给单个数据类型提供多个注入方式的注入方法,注入代码如下:
可以看到只是比给 simpleData 注入的时候多了两个步骤:@Qualifier @Retention(AnnotationRetention.BINARY) annotation class EmunA @Qualifier @Retention(AnnotationRetention.BINARY) annotation class EmunB @Module @InstallIn(SingletonComponent::class) class EmunDataModule { @EmunA @Provides fun provideEmunA() = EmunData("EmunA") @EmunB @Provides fun provideEmunB() = EmunData("EmunB") }- 定义 @EmunA 和 @EmunB 两个注解
- 给两个 @Provides 修饰的方法分别加上了 @EmunA 和 @EmunB 两个注解
这两个步骤就可以在注入的时候选择是通过 provideEmunA 方法注入还是通过 provideEmunB 方法注入
总结
至此,Hilt的基本用法已经大致了解了。个人感觉Hilt的优势主要体现在两点上:
- 在和其他的Jetpack组件一起使用的时候可以有加成,像生命周期的管理,Android类的原生支持之类的。
- 完全由注解来构建依赖关系,不需要额外的代码来构建,加上AndroidStudio的强大跳转支持,可以很快定位到依赖关系。
不过也有些缺点:
- 完全使用注解的方式来定位依赖关系,会导致看代码不能知道到底是如何注入的,特别是依赖关系一多,就不是很明了。需要借助AndroidStudio才能快速找到。
- 学习注解的成本很大
- 会自动生成大量的注入代码,增加APK体积
个人还是觉得Kotlin就用Koin吧!(#.#)
本文介绍了Android Hilt的依赖注入原理和使用步骤,包括添加依赖项、使用必要注解以及各种注入方式。虽然Hilt提供了与Jetpack组件的良好集成,但作者建议考虑使用Koin,因其简洁明了。Hilt的优势在于与Android类的原生支持,但完全注解的方式可能导致代码可读性下降和APK体积增大。
1599

被折叠的 条评论
为什么被折叠?



