作用域Scope
在Koin中,Scope
是用于管理依赖项的对象。它可以定义特定Scope
内的依赖关系,并在该Scope
中跟踪和重用这些依赖关系,在指定的Scope
中,只会生成唯一的一个实例。Koin已经默认帮我们定义了三个常用的Scope
类,如ScopeActivity
、RetainedScopeActivity
和ScopeFragment
,它们都是实现了AndroidScopeComponent
接口。
ScopeActivity
保证在当前的Activity
中只会注入同一个相关的实例,但是在旋转屏幕时,它会重新生成一个实例;RetainedScopeActivity
保证在当前的Activity
中只会注入同一个相关的实力,并且在旋转屏幕时,注入的还是之前的那个实例,Koin在这种情况下也是通过ViewModel
来实现的;ScopeFragment
的作用同ScopeActivity
。
下面我们来看看如何使用这些作用域组件。
ScopeActivity
首先我们定义个一个Activity
,继承自ScopeActivity
,并在内部注入两个ScopeTest
对象,看看这两个对象是否为同一个实例
class MainActivity : ScopeActivity() {
// 注入第一个对象
private val scopeTest by inject<ScopeTest>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
scopeTest.test()
// 再次注入这个对象
val test: ScopeTest = scope.get()
test.test()
}
override fun onCloseScope() {
super.onCloseScope()
Log.d(TAG, "onCloseScope")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
}
}
// 为测试注入对象
class ScopeTest {
fun test() {
Log.d(TAG, "ScopeTest test: ${this.hashCode()}")
}
}
val scopeModule = module {
// 使用scope和scoped来注入ScopeTest对象,并且作用域为MainActivity
scope<MainActivity> {
scoped {
ScopeTest()
}
}
}
运行App看一下两个对象输出的HashCode
是否相同:
ScopeTest test: 36979086
ScopeTest test: 36979086
从日志中可以看出,只要在MainActivity
中,无论何时在何处注入ScopeTest
对象,都是为同一个实例,这就是scope
的作用,保证在我们定义的作用域范围内只会注入一个相同的实例。
在使用scope
来限定作用域时,我们传入的作用域对用的类必须是实现了AndroidScopeComponent
接口,此接口内部就一个scope
对象和onCloseScope()
方法
interface AndroidScopeComponent {
//TODO Make scope nullable with var?
/**
* Current Scope in use by the component
*/
val scope: Scope
/**
* Called before closing a scope, on onDestroy
*/
fun onCloseScope(){}
}
scope
是为了帮助我们注入对象和绑定当前作用域生命周期而存在,onCloseScope()
方法是回调当前作用域关闭的作用。具体后面我们分析下ScopeActivity
的实现,看看它是如何定义的。
RetainedScopeActivity
Retained
的字面意思就是保存、保留的意思,它的作用就是在当前Activity
人为或者意外被迫销毁重建时,还可以保留当前注入的实例。
class RetainActivity : RetainedScopeActivity() {
// 依旧是注入ScopeTest实例
private val scopeTest by inject<ScopeTest>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
scopeTest.test()
// 第二次注入ScopeTest实例
val test: ScopeTest = scope.get()
test.test()
}
}
# 注入方法还是和ScopeActivity一样
val scopeModule = module {
// 只是此处传入的作用域对象变成了RetainActivity
scope<RetainActivity> {
scoped { ScopeTest() }
}
}
日志部分我们为两部分看,第一步看看第一次打开此界面时,两次日志是否输出同一个实例
ScopeTest test: 48843391
ScopeTest test: 48843391
onDisplayChanged: 横屏
在横屏的时候,两次日志输出的HashCode
确实是相同的,证明了只要在此界面中,注入的ScopeTest
都是同一个实例,此时RetainedScopeActivity
和ScopeActivity
是相同的作用,接下来我们再把屏幕旋转成竖屏看看日志。
ScopeTest test: 48843391
ScopeTest test: 48843391
onDisplayChanged: 横屏
ScopeTest test: 48843391
ScopeTest test: 48843391
onDisplayChanged: 竖屏
日志输出和我们预期的是同一个结果,无论是横屏还是竖屏的情况下,ScopeTest
被注入的都是同一个实例,在文章开头的地方我们就提到过,它是通过创建一个ViewModel
来保证旋转屏幕之后还是注入之前的实例,具体源码本次就不详细介绍了,可以通过/org/koin/androidx/scope/ComponentActivityExt.kt:83
来了解一下。
ScopeActivity实现
ScopeActivity
源码还是比较简单的,直接看代码
abstract class ScopeActivity(
@LayoutRes contentLayoutId: Int = 0,
) : AppCompatActivity(contentLayoutId), AndroidScopeComponent {
override val scope: Scope by activityScope()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkNotNull(scope)
}
}
- 实现了
AndroidScopeComponent
接口 - 覆写了
scope
对象,通过懒加载的形式初始化
下面直接进入activityScope()
方法,看看它是如何创建Scope
对象的
// 通过懒加载调用createActivityScope()方法
fun ComponentActivity.activityScope() = lazy { createActivityScope() }
fun ComponentActivity.createActivityScope(): Scope {
if (this !is AndroidScopeComponent) {
error("Activity should implement AndroidScopeComponent")
}
// 通过createScopeForCurrentLifecycle()创建一个绑定生命周期的Scope对象
return getKoin().getScopeOrNull(getScopeId()) ?: createScopeForCurrentLifecycle(this)
}
internal fun ComponentCallbacks.createScopeForCurrentLifecycle(owner: LifecycleOwner): Scope {
val scope = getKoin().createScope(getScopeId(), getScopeName(), this)
scope.registerCallback(object : ScopeCallback{
override fun onScopeClose(scope: Scope) {
(owner as AndroidScopeComponent).onCloseScope()
}
})
owner.registerScopeForLifecycle(scope)
return scope
}
最终还是通过createScopeForCurrentLifecycle()
方法来具体创建Scope
对象。
此方法主要就是为了绑定Activity
的生命周期,并且在registerCallback
回调中感知Activity
的onDestroy()
时机,同时在界面销毁时清除作用域的绑定关系。
到这为止,Koin的一些日常使用已经呈现出来了,大家可以自行体验下Koin依赖注入使用的简单和便捷!
转载:https://blog.csdn.net/YoungOne2333/article/details/131576065