Hilt加强篇:理解Component和Scoped
Posted microhex
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hilt加强篇:理解Component和Scoped相关的知识,希望对你有一定的参考价值。
1. 历史
上次写过一遍关于Hilt
的使用偏,里面详细介绍了Hilt
历史和应用,链接对应如下:
如果没有对Hilt
的使用有些了解,那就先入门看看,谢谢。
2. Component
Component
和Scope
的英文名分别是组件
和范围
的意思,第一次见到这个两个注解的时候,还是有些不清楚的。下面慢慢说,Hilt-android
中存在8个Component
和对应的8个Scope
,先来聊一聊这个8个Component
,分别为:
序列 | 名称 | 创建时间 | 销毁时间 |
---|---|---|---|
1 | SingletonComponent | Application#onCreate() | Application#onDestroy() |
2 | ActivityRetainedComponent | Activity#onCreate() | Activity#onDestroy() |
3 | ViewModelComponent | ViewModel created | ViewModel destroyed |
4 | ActivityComponent | Activity#onCreate() | Activity#onDestroy() |
5 | FragmentComponent | Fragment#onAttach() | Fragment#onDestroy() |
6 | ViewComponent | View#super() | View destroyed |
7 | ViewWithFragmentComponent | View#super() | View destroyed |
8 | ServiceComponent | Service#onCreate() | Service#onDestroy() |
下面我们用一个例子,来说明一下Component
的含义:
定义一个AppData
的数据类:
class AppData
定义一下我们的Module
类型:
@Module
@InstallIn(SingletonComponent::class)
object AppDataModule
@Provides
fun provideAppData() : AppData = AppData()
可以看出,我们的Module
是被SingletonComponent
所修饰的,通过查表,我们发现凡是被SingletonComponent
修饰的,其生命周期是在Application
的生命周期内的,意思是伴随着整个App出生到死去(暂时不考虑多线程),那么我们可以在App
的任意位置使用注入技术,获取到AppData
实例。现在我们定义两个Activity
,分别为:AppUIOne
和AppUITwo
,代码分别为:
AppUIOne.kt
@AndroidEntryPoint
class AppUIOne : AppCompatActivity()
@Inject
@JvmField
var mAppData : AppData? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.app_ui_one_layout)
Log.d("TAG","first page : $mAppData")
fun nextPage(v : View)
startActivity(Intent(this, AppUITwo::class.java))
剩下的AppUITwo
为:
@AndroidEntryPoint
class AppUITwo : AppCompatActivity()
@Inject
@JvmField
var mAppData : AppData? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
Log.d("TAG","second page : $mAppData")
代码都很类似,只是在Activity
中注入了AppData
实例,我们可以看一下结果:
我们可以看到,注入成功了。
知道了SingletonComponent
,我们可以照葫芦画瓢,再写一个比较常见的ActivityComponent
样例;
定义我们的数据类:ActivityData
:
class ActivityData constructor(private val info : String)
constructor() : this("$System.currentTimeMillis()_data")
override fun toString(): String
return "ActivityData(info='$info')"
此时,再去定义一下我们的Module
:
@Module
@InstallIn(ActivityComponent::class)
object ActivityInstallModule
@Provides
fun provideActivityData() : ActivityData = ActivityData()
然后定义一下我们的ActivityComponentUI
:
class ActivityComponentUI : AppCompatActivity()
@Inject
@JvmField
var appData : AppData? = null
@Inject
@JvmField
var mActivityData : ActivityData? = null
@Inject
@JvmField
var mActivityData2 : ActivityData? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(View(this))
Log.d("TAG","--show the activityData------>>\\n$appData--->>\\n$mActivityData\\n---->>$mActivityData2")
输入一下结果:
可以看得到,我们的AppData
和两个ActivityData
都被赋值了,说明Hilt
起作用了。
我们已经看到了Component
的效果,下面我来解释一下Component
的实际作用。Hilt
是基于Dagger2
的,现在应用于Android
设备上,Android
的组件有着本身的生命周期,那么通过Component
组件来界定我们注入的组件的生命周期,什么意思呢?我们举个简单的例子:
@Module
@InstallIn(ActivityComponent::class)
object ActivityInstallModule
@Provides
fun provideActivityData() : ActivityData = ActivityData()
现在ActivityData
是被限定在ActivityComponent
中的,那么意味着目标Acticity
的被注入对象var mActivityData : ActivityData?=null
是在Activity
的onCreate
方法中被创建,在调用onDestory
之前,mActivityData
被销毁,是和Activity
共用同一个生命周期,伴随着Activity
同生共死。又由于组件的生命周期的长度存在着包含关系,比如App
的生命周期包含于Activity
的生命周期,Activity
的生命周期包含于在Activity
内的Fragment
的生命周期,所以对应的Component
存在着继承关系,继承图为:
对于这个继承有什么用呢?其实上面的代码中已经说明了问题,我们可以在Activity
中使用SingleComponent
组件中提供的注入对象,同样,在含有Fragment
的Activity
中,Fragment
中不仅可以获取到SingleComponent
提供的注入对象,同时也可以获取到ActivityComponent
提供的注入对象,当然也可以获取到由FragmentComponent
提供的注入对象,但是不能获取由ViewWithFragmentComoponent
。因为继承是不可逆的。
3. scope
把我们的Component
了解完,现在来说说Scope
逻辑了。明面上的范围
是什么意思呢?还是以例子说明吧。
我们记得上面appData
的例子,再来重温一下打印结果:
我们发现两个AppData
的打印结果是不一样的,一个是8b035bd
,一个是5b704d8
,不同的toString()
表明两个是不同的对象。如果我们需要在所有的Activity
中获取的AppData
都是同一个对象,换句话的意思是我们需要整个App中只存在一个AppData
实例,那么全局单例应该怎么生成呢?其实也很简单,只需要添加一个@Singleton
注解:
@Module
@InstallIn(SingletonComponent::class)
object AppDataModule
@Provides
@Singleton
fun provideAppData() : AppData = AppData()
在provideAppData
上面添加一个@Singleton
注解,然后我们再次运行代码:
我们可以看到,first page
和second page
中对应的AppData
都是指向了同一个地址,说明了AppData
在两个Activity
中只保存了一个对象。
我们在ActivityInstallModule
中添加另外一个Scope
注解ActivityScoped
:
@Module
@InstallIn(ActivityComponent::class)
object ActivityInstallModule
@Provides
@ActivityScoped
fun provideActivityData() : ActivityData = ActivityData()
然后同时再在Activity
中获取ActivityData
数据:
@AndroidEntryPoint
class ActivityComponentUI : AppCompatActivity()
@Inject
@JvmField
var mActivityData : ActivityData? = null
@Inject
@JvmField
var mActivityData2 : ActivityData? = null
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(View(this))
Log.d("TAG","--show the activityData--->>\\n$mActivityData---->>\\n$mActivityData2")
然后我们看一下输出结果:
发现二者是指向的是同一个对象,这样说的不死很清楚?我们把图整理对比一下:
module | 结果 |
---|---|
我们发现,添加ActivityScoped
的区别就是Activity
中被注入的对象是否为同一个
,换句话说是在Component
的生命周期范围内,如果被注入的对象添加了Scope
注解,那么它将在生命周期范围内只会生成一个唯一对象。
如果SingletonComponent
组件中添加了Singleton
注解,那么App整个生命周期内只存在一个对象;
如果ActivityComponent
组件中添加了ActivityScoped
注解,那么在每一个Activity
生命周期范围内只存在一个对象。
这就是Scope
的本质意义!!!
当然了,Scope
也不是随便写的,每一个Component
对应唯一一个Scope
,不能写错,也没有继承的概念,只能一一对应,如果写错,编译时就会直接报错。那么有哪些,可以看一下这个表:
Component | Scope |
---|---|
SingletonComponent | @Singleton |
ActivityRetainedComponent | @ActivityRetainedScoped |
ViewModelComponent | @ViewModelScoped |
ActivityComponent | @ActivityScoped |
FragmentComponent | @FragmentScoped |
ViewComponent | @ViewScoped |
ViewWithFragmentComponent | @ViewScoped |
ServiceComponent | @ServiceScoped |
4. 总结
Component
组件的意义:绑定了被注入对象的生命周期;
Scope
组件的意义:定义了被注入对象在生命周期内是否唯一。
大概过程我们走了一遍,不知道自己有没有将清楚呢。最后,上一张官方的原图:
然后大家可以上上一下官方网站,然后可以聊聊你的理解和认识。
官方网站:
https://dagger.dev/hilt/components.html
以上是关于Hilt加强篇:理解Component和Scoped的主要内容,如果未能解决你的问题,请参考以下文章
[Vue @Component] Pass Props Between Components with Vue Slot Scope