在上一节中,咱们简略介绍了Dagger2的运用,其实咱们在运用Dagger2的时分,发现仍是比较繁琐的,要自己写Module、Component、Provides等等,于是Hilt的团队就和Dagger2的团队一起,规划了面向Android移动端的依靠注入框架 — Hilt
1 Hilt配置
在项目级的build.gradle中,引进支持hilt的插件,留意官方文档中的2.28-alpha版别可能有文件,主张运用下面的版别
classpath "com.google.dagger:hilt-android-gradle-plugin:2.43.2"
app的build.gradle中引进插件
id 'dagger.hilt.android.plugin'
引进依靠
implementation "com.google.dagger:hilt-android:2.43.2"
kapt "com.google.dagger:hilt-android-compiler:2.43.2"
2 Hilt的运用
首要按照常规,先写一个Module
@Module
class RecordModule {
@Provides
fun providerRecord():Record{
return Record()
}
}
假如是Dagger2的写法,需求再写一个Component,将RecordModule加载进去,那么Hilt就不要这一步,而是需求一个注解InstallIn来声明这个Module运用在哪个当地
@InstallIn(ApplicationComponent::class)
@Module
class RecordModule {
@Provides
fun providerRecord(): Record {
return Record()
}
}
在Hilt中有以下几个Component,我这儿拿几个典型说一下
首要ApplicationComponent,它会存在整个App生命周期中,随着App的毁掉而毁掉,也就意味着,在App的任何方位都能够运用这个Module
//A Hilt component that has the lifetime of the application
@Singleton
@DefineComponent
public interface ApplicationComponent {}
像ActivityComponent,肯定便是存在于整个Activity生命周期中,随着Activity的毁掉而毁掉
//A Hilt component that has the lifetime of the activity.
@ActivityScoped
@DefineComponent(parent = ActivityRetainedComponent.class)
public interface ActivityComponent {}
其他的都类似,都是随着组件的生命周期结束而消逝。
例如咱们需求在MainActivity中注入某个类,那么需求运用@AndroidEntryPoint润饰,代表当时依靠注入的切入点
@AndroidEntryPoint
class MainActivity : AppCompatActivity()
同时,需求将当时app界说为Hilt App
@HiltAndroidApp
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
}
}
并且,在MainActivity中注入这个目标之后,也不需求像Dagger那样,去创建具体的Component目标
@Inject
@JvmField
var record:Record? = null
这样一看,Hilt是不是要比Dagger要简略许多了。
2.1 部分单例
@InstallIn(ActivityComponent::class)
@Module
class RecordModule {
@Provides
@ActivityScoped
fun providerRecord(): Record {
return Record()
}
}
假如咱们期望Record是一个单例目标,能够运用@ActivityScoped注解润饰,咱们先看下效果
@Inject
@JvmField
var record: Record? = null
@Inject
@JvmField
var record2: Record? = null
在MainActivity中声明了两个目标,咱们发现两个目标的hashcode是共同的,阐明在当时Activity中这个目标便是单例,可是跳转到下一个Activity的时分,发现拿到的目标便是一个新的目标
2022-09-11 20:52:19.583 1860-1860/com.lay.image_process E/TAG: record 83544912record2 83544912
2022-09-11 20:53:11.071 1860-1860/com.lay.image_process E/TAG: record 163680212
也便是说,@ActivityScoped润饰的目标仅仅部分单例,并不是大局的;那么怎么才能拿到一个大局的单例呢?其实在之前的版别中,有一个ApplicationComponent,其对应的效果域@Singleton拿到的目标便是大局单例,后来Google给移除了,我觉得Google之所以移除,可能便是推动咱们选用数据共享规划模式。
Component | 效果域(部分单例) |
---|---|
ActivityComponent | ActivityScoped |
FragmentComponent | FragmentScoped |
ServiceComponent | ServiceScoped |
ViewComponent | ViewScoped |
ViewModelComponent | ViewModelScoped |
上面是收拾的运用比较频繁的Component,对应的部分单例效果域
2.2 为接口注入完成类
首要创建一个接口
interface MyCallback {
fun execute()
}
然后创建一个完成类,这儿需求留意,构造办法中假如需求传入上下文,那么需求运用@ApplicationContext润饰,其他参数则不需求
class MyCallBackImpl : MyCallback {
private var context: Context? = null
@Inject
constructor(@ApplicationContext context: Context) {
this.context = context
}
override fun execute() {
Toast.makeText(context, "完成了", Toast.LENGTH_SHORT).show()
}
fun clear() {
if (context != null) {
context = null
}
}
}
那么在创建module的时分,办法需求界说为笼统办法,并且需求运用@Binds来获取完成类,办法相同是笼统办法,参数为具体完成类,回来值为接口。
@Module
@InstallIn(ActivityComponent::class)
abstract class ImplModule {
@Binds
abstract fun getImpl(impl: MyCallBackImpl): MyCallback
}
那么在运用时,就非常简略了,这儿获取到的便是MyCallBackImpl完成类
@Inject
@JvmField
var callback: MyCallback? = null
那么咱们想一个问题,假如我有多个完成类,那么怎么区别这个MyCallback到底是哪个完成类呢?相同能够运用注解来区别
class MyCallbackImpl2 : MyCallback {
private var context: Context? = null
@Inject
constructor(@ApplicationContext context: Context) {
this.context = context
}
override fun execute() {
Toast.makeText(context, "完成2", Toast.LENGTH_SHORT).show()
}
}
这样的话,就有两个笼统办法,别离回来MyCallbackImpl2和MyCallBackImpl两个完成类,那么在区别的时分,就能够经过@BindImpl和@BindImpl2两个注解区别
@Module
@InstallIn(ActivityComponent::class)
abstract class ImplModule {
@Binds
@BindImpl
abstract fun getImpl(impl: MyCallBackImpl): MyCallback
@Binds
@BindImpl2
abstract fun getImpl2(impl2: MyCallbackImpl2): MyCallback
}
在调用时,相同需求运用@BindImpl2或者@BindImpl来区别获取哪个完成类
@Inject
@JvmField
@BindImpl2
var callback: MyCallback? = null
其实@BindImpl注解很简略,便是经过@Qualifier注解来区别
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class BindImpl2 {
}
3 Hilt原理
咱们看到在调用这个注入目标的时分,发现没有看到对应的进口,并没有DaggerComponent那么显眼,其实编译时技术许多都是这样,是要从打包编译文件夹去找
咱们能够看到,hilt是自己独自的一个文件夹,其间就有生成的资源文件
private MyCallbackImpl2 myCallbackImpl2() {
return new MyCallbackImpl2(ApplicationContextModule_ProvideContextFactory.provideContext(singletonCImpl.applicationContextModule));
}
private MainActivity injectMainActivity3(MainActivity instance) {
MainActivity_MembersInjector.injectRecord(instance, providerRecordProvider.get());
MainActivity_MembersInjector.injectRecord2(instance, providerRecordProvider.get());
MainActivity_MembersInjector.injectCallback(instance, myCallbackImpl2());
return instance;
}
其实咱们能够看到,这种完成方式跟Dagger2其实是相同的,相同都是在内部初始化了某个类,例如MyCallbackImpl2,其context是由ApplicationContextModule供给的
@InjectedFieldSignature("com.lay.image_process.MainActivity.callback")
@BindImpl2
public static void injectCallback(MainActivity instance, MyCallback callback) {
instance.callback = callback;
}
调用injectCallback便是将MainActivity中的callback赋值,获取的便是MyCallbackImpl2完成类。
其实Hilt的内部完成原理跟Dagger2是相同,仅仅做了进一步的封装,所以假如理解了之前Dagger2的原理,比较Hilt也不在话下了。