什么是辅助注入
介绍辅助注入
辅助注入,如字面意思,就是辅助的依赖注入。这个辅助,其实是由开发者,也就是我们来辅助Hilt来进行依赖注入。当然这么看起来也是很懵,看一下下面的使用场景介绍应该就能懂个大概了。
辅助注入的使用场景
而辅助注入的使用场景呢?
我们都知道,依赖注入框架可以帮助我们创建实例对象。不过在使用Hilt(或者Dagger,作为一个Android开发,我在下文中都会称为Hilt)这个框架之前,我一直有一个困惑,那就是Hilt是怎么给我创建实例的。
别的时候都还好说,如果创建实例时,需要传入一个可变的var的参数。比如我要创建一个花的对象,需要在创建时就传入一个花的名字:
var name:String = "Rose"
val flower:Flower = FLower(name)
而交给Hilt要怎么创建呢?毕竟参数是可变的,Hilt怎么知道这个参数是什么呢?
@Inject
latinit var flower:Flower

这个时候就需要用到辅助注入了。
所以辅助注入的使用场景可以概括为:
- 创建实例对象需要参数
- 参数是可变的(不一定是
var修饰的,val也可以,主要是想说这个参数不是固定的不变的,否则就能通过@Provide来创建了)
至于其他场景,比如不需要参数啊,参数是静态不变的,又可以根据不同的类型进行不同的处理,这里就不再赘述,可以看一下我的文章:
辅助注入的发展
辅助注入也经历过时代的发展,在Dagger的早期版本中,是通过@AutoFactory的形式进行辅助注入的。而在Dagger 2.31+版本之后,则是用@AssistedInject。对于Hilt也是一样,使用前一定要检查自己的依赖版本是否支持最新的Assisted Injection。
如何进行辅助注入
准备工作
和其他方式的依赖注入一样,准备工作分为两步。
导入依赖库
implementation "com.google.dagger:hilt-android:2.37"
kapt "com.google.dagger:hilt-android-compiler:2.37"
配置Application
@HiltAndroidApp
class MyApplication : Application()
记得把自定义的Application在Manifest配置一下。
普通类的辅助注入
定义一个带参数的类
在完成准备工作后,我们要先创建一个需要辅助注入的类,这个类的构造方法中包含一个String的参数
class ImageLoader @AssistedInject constructor(
@Assisted
private val imgUrlString: String
) {
override fun toString(): String = imgUrlString
}
可以看到,与其他类型的依赖注入使用@Inject来标记构造方法不同,辅助注入使用的注解是@AssistedInject。此外,对于需要传入的参数,要用@Assisted来注解。很明显,@AssistedInject是告诉Hilt这个类要进行辅助注入,而@Assisted是告诉Hilt这个参数是需要传入的参数。
定义一个Factory接口
仅仅这样,Hilt也很难给我们直接创建ImagerLoader的实例对象,这里还需要一个辅助的工厂来生成ImageLoader的实例。
@AssistedFactory
interface ImageLoaderFactory {
fun createImageLoader(imgUrlString: String): ImageLoader
}
在上面的代码中,创建了一个接口,并在接口上使用了AssistedFactory注解。在接口内有一个抽象方法,用于生成ImageLoader类的实例。这个抽象方法名是无所谓的,可以随便取。但是里面的参数,一定要与ImageLoader构造方法中的参数一一对应。最后,方法的返回类型为ImageLoader。到这里就可以进行辅助注入了。
进行辅助注入
我们选一个合适的入口点,我这里就是Activity。就像其他类型的依赖注入一样,要给Activity一个入口点的标记@AndroidEntryPoint,然后再定义一个factory的变量。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var imageLoaderFactory: ImageLoaderFactory
}
通过@Inject注解,这个factory变量的创建就交给了Hilt,接着就是最后ImageLoader对象的创建。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageLoader = imageLoaderFactory
.createImageLoaderFactory("https://dagge.dev/")
}
在代码中,调用了factory的create方法传入了所需要的参数,最后返回了ImageLoader的实例对象。到这里,就完成了一个需要动态可变参数的对象的辅助注入。
进阶:多个参数的辅助依赖注入
当构造函数内的参数不止一个时,如果参数类型各不相同,切记Factory中create方法中的参数要与其一一对应。
当参数类型有相同的时候,就需要通过别名来进行区分。
class MyDataService @AssistedInject constructor(
dataFetcher: DataFetcher,
@Assisted("server") serverConfig: Config,
@Assisted("client") clientConfig: Config
) {}
@AssistedFactory
interface MyDataServiceFactory {
fun create(
@Assisted("server") serverConfig: Config,
@Assisted("client") clientConfig: Config
): MyDataService
}
需要注意的是,Dagger官网中提到,此方法并不是一定有效。
ViewModel的辅助注入
我们知道,Hilt是Dagger的Android定制版,所以对于一些Android开发经常用到的组件就进行了优化,ViewModel就是这样。
准备工作
除了前面准备工作中导入依赖库和定义Application,辅助注入ViewModel时还需要做些额外的工作。
implementation "androidx.activity:activity-ktx:1.2.2"
其实就是多个库,这是Kotlin对Activity的支持,有了它就能在Activity中使用lateinit。
定义带参数的ViewModel
做完了以上的准备工作,就能开始上手了。首先就是要定义一个带参数的ViewModel。
class MyViewModel @AssistedInject constructor(
@Assisted val name: String
) : ViewModel()
可以看到和普通类的定义是类似的,都是通过@AssistedInject注解修饰构造方法,通过@Assisted注解修饰要传入的参数。
定义Factory接口
@AssistedFactory
interface ViewModelFactory {
fun createViewModel(name: String): MyViewModel
}
与普通类一样,也是定义一个Factory接口,使用AssistedFactory注解修饰,里面有一个createViewModel方法,方法中有要传入的参数,方法的返回类型为MyViewModel。
定义provideFactory方法
这里就是ViewModel与普通类不同的地方了,利用Kotlin函数即对象的特性,直接定义了一个方法,这个方法不在任何类或接口内。
fun provideFactory(
assistedFactory: ViewModelFactory,
name: String
): ViewModelProvider.Factory =
object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return assistedFactory.createViewModel(name) as T
}
可以看到,方法有两个参数,一个是Factory接口,一个是ViewModel所需要传入的参数。方法最后返回了ViewModelProvider.Factory,这个就是最后创建ViewModel的关键。
在provideFactory方法中,则是直接创建了一个匿名类,类内又重写了create方法,返回类型为我们要创建的ViewModel。
在create方法中,又调用我们创建的Factory接口中的createViewModel方法,最终返回ViewModel。
进行辅助注入
和普通类一样,我们在Activity中进行ViewModel的辅助注入。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var viewModelFactory: ViewModelFactory
val viewModel: MyViewModel by viewModels {
provideFactory(
viewModelFactory,
"Assisted Inject"
)
}
}
和普通类一样,要定义一个ViewModelFactory变量,加上@Inject注解,交给Hilt来创建Factory。与普通类不同的是,ViewModel可以通过Kotlin的特性直接lateinit,通过by viewModels{}就可以延迟初始化,而在viewModels{}中传入ViewModelProvider.Factory即可进行ViewModel的创建,而我们之前定义的provideFactory方法的返回类型就是ViewModelProvider.Factory,所以直接将方法传入即可。
再给方法传入参数,第一个是Hilt创建的Factory对象,第二个是动态的参数,到这里ViewModel的辅助注入完成。

结尾
在学习辅助注入的过程中我遇到很多问题,我是通过Google的SunFlower这个项目来学习Jetpack的,其中就用到了辅助注入。但是Sunflower项目中使用的还是旧版本的辅助注入,依赖库还是squreup那个版本的。我按照sunflower上的代码,看着Dagger官网的教程,导入的是Hilt最新的库,最后直接混乱,一直没法正常编译。
而且国内关于这方面的内容也很少,一直卡着没什么头绪,搞了快一周。最后偶然搜到了一篇国外的的教程,总算是解了我的困惑,其实使用起来不难,就是没有一个合适的教程而已。
所以我把那个教程翻译了一下,另外附上Dagger官网的翻译,都附在下面。
【译文】使用Dagger和Hilt辅助注入
【译文】Dagger辅助注入
另外提一嘴,Dagger在依赖库的管理上真的太混乱了。在上面我翻译的教程中使用的是Hilt的2.33-beta版。
implementation "com.google.dagger:hilt-android:2.33-beta"
kapt "com.google.dagger:hilt-android-compiler:2.33-beta"
而我刚开始使用的是我查到的Hilt的最新版2.37
implementation "com.google.dagger:hilt-android:2.37"
kapt "com.google.dagger:hilt-android-compiler:2.37"
我按照翻译的教程进行ViewModel的辅助注入时,编译时就会报错:
Hilt_MyApplication.java:20: 错误: 找不到符号
return DaggerMyApplication_HiltComponents_SingletonC.builder()
^
而我把版本降为2.33-beta就能正常编译,真是日了狗了。最后发现,原来最新版本中,不再需要导入hilt-lifecycle-viewmodel库,要把下面的库删掉。
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
因为这个又耽误我好久。
本文详细介绍了Dagger/Hilt的辅助注入,解释了其概念、使用场景和历史演变。通过逐步指导,展示了如何进行普通类和ViewModel的辅助注入,包括多个参数的处理。同时,分享了在学习过程中遇到的问题和解决方案,为读者提供了一个清晰的实践指南。
457

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



