史蒂夫科恩
学习Android开发 (Learning Android Development)
If you are familiar with Dagger 2, you probably know that Scope an important part of Dependency Injection. It enables us to determine if we are getting the same dependent object or a new one.
如果您熟悉Dagger 2,您可能知道Scope是依赖注入的重要组成部分。 它使我们能够确定要获取相同的从属对象还是新的对象。
What about Koin, the popular Kotlin way of getting your dependencies? The scope documentation for core and android Koin is a good start, but I spent some time digging out all the gist of it. Here I provide some illustration, and some gotcha to watch out.
Koin是如何获得依赖的流行Kotlin方法呢? 核心和android Koin的范围文档是一个不错的开始,但是我花了一些时间来挖掘它的全部要旨。 在这里,我提供一些说明,以及一些需要注意的地方。
单人和工厂 (Single and Factory)
The simplest scope available in Koin is single
and factory
.
Koin中最简单的范围是single
和factory
。
val koinModule =module {single(qualifier = named("singleName")) { Dependency() }factory(qualifier = named("factoryName")) { Dependency() }
}
To illustrate this easily, I use the same Dependency
object for both single
and factory
scope. The are illustrated below
为了方便地说明这一点,我对single
作用域和factory
作用域使用相同的Dependency
对象。 如下图所示
Single — always provide the same copy of
Dependency
object for the requester单个-始终为请求者提供相同的
Dependency
对象副本Factory — always provide a new copy of Dependency object for the requester
工厂-始终为请求者提供Dependency对象的新副本
To get the dependency
, (as I qualified it with proper names), it is as below
要获取dependency
(我用专有名称限定它),如下所示
val alwaysSameDependency = get<Dependency>(
qualifier = named("singleName")).toString()val alwaysNewDependency = get<Dependency>(
qualifier = named("factoryName")).toString()
知道了! (Gotcha!)
The single
might sounds like it’s a Singleton. It is not really one, unless it is generated on the root module.
该single
力量的声音就像是一个单例 。 除非它是在根模块上生成的,否则它不是真正的一个。
For the case of it is in a loaded module as loaded
如果是在已加载的模块中
loadKoinModules(koinModule)
upon the lifetime of the module is loaded, it will always generate the same copy. However if the module is unloaded as below
在加载模块的生命周期时,它将始终生成相同的副本。 但是,如果按以下方式卸载模块
unloadKoinModules(koinModule)
and then reloaded later, a new copy of the single
dependency will be created instead.
然后再重新加载,将改为创建single
依赖项的新副本。
合格范围 (Qualified Scope)
Both single
and factory
live throughout the lifetime of the koin module loaded. However if we want some dependencies that has a scope differ from the module lifetime, we could use Qualified Scope. There are 2 of them.
在装入koin模块的整个生命周期中, single
人和factory
都可以使用。 但是,如果我们希望某些依赖项的作用域与模块生存期不同,则可以使用合格作用域。 有两个。
字符串合格范围 (String Qualified Scope)
Instead of dependent on the Module solely, we could create a Scope independently from the Module Lifetime.
我们可以创建一个独立于模块生命周期的作用域,而不是仅依赖模块。
val koinModule =module {scope(named("AScopeName")) {scoped(qualifier = named("scopedName")) { Dependency() }factory(qualifier = named("factoryName")) { Dependency() }
}
}
As we could see above, we have a scope
with the name of AScopeName
. To create the scope within the koinModule
, just need to do the following.
正如我们在上面看到的,我们有一个名称为AScopeName
的scope
。 要在koinModule
创建范围,只需执行以下操作。
val stringQualifiedScope = getKoin().createScope(
"ScopeNameID", named("AScopeName"))
The
ScopeNameID
is the ScopeID that one could use to identify the Scope from one to the other. It is used internally as the Key (ID) to look for this scope. It’s used is described later.
ScopeNameID
是ScopeID 一个人可以用来识别一个范围到另一个范围。 它在内部用作查找此范围的键(ID)。 稍后将对其进行描述。
Here we have scoped
and factory
.
在这里,我们确定了scoped
和factory
。
Scoped — always provide the same copy of
Dependency
object from the Scope for the requester作用域-始终为请求者提供作用域中相同的
Dependency
对象副本Factory — always provide a new copy of Dependency object from the Scope for the requester
Factory —始终从范围为请求者提供Dependency对象的新副本
To get the dependencies, is as below
要获取依赖关系,如下
val alwaysSameDependency = stringQualifiedScope.get<Dependency>(
qualifier = named("scopedName")).toString()val alwaysNewDependency = stringQualifiedScope.get<Dependency>(
qualifier = named("factoryName")).toString()
To close away the scope (to end it life), one just need to call close
as below
要关闭示波器(以终止使用寿命),只需按以下方式调用close
stringQualifiedScope.close()
类型合格范围 (Type Qualified Scope)
Type qualified scope is same as String Qualifies Scope with the exception it is not tied to a String named made up scope, but a variable type.
类型限定范围与字符串限定范围相同,不同的是它不是绑定到由命名范围组成的字符串,而是变量类型。
In example below, we have a class type Container
, where once we have the object, we could get scope from it.
在下面的示例中,我们有一个类类型Container
,在其中有了对象后,就可以从中获取作用域。
val koinModule =module {factory { Container() } scope<Container> {scoped(qualifier = named("scopedName")) { Dependency() }factory(qualifier = named("factoryName")) { Dependency() }
}
}
Actually TypeQualifiedScope is a little misnomer. It is actually differs from Object to Object even if they are of the same type (i.e. two
实际上, TypeQualifiedScope有点用词不当。 实际上,即使对象的类型相同(即两个
So to create the scope, we first need to get the Container object first.
因此,要创建范围,我们首先需要首先获取Container对象。
val container: Container = get()
val typeQualifiedScope = container.scope
To get the dependencies, is the same as stringQualifiedScope above.
要获取依赖项,与上面的stringQualifiedScope相同。
val alwaysSameDependency = typeQualifiedScope.get<Dependency>(
qualifier = named("scopedName")).toString()val alwaysNewDependency = typeQualifiedScope.get<Dependency>(
qualifier = named("factoryName")).toString()
To close away the scope (to end it life), one just need to call close
as below
要关闭示波器(以终止使用寿命),只需按以下方式调用close
typeQualifiedScope.close()
知道了! (Gotcha!)
-
When getting scope from object e.g.
container.scope
, it is actuallycontainer.getOrCreateScope()
, so if one usescontainer.scope.close()
,container.scope
still works as it create a new one, making one confuse why after closing, it could still be used从对象(例如
container. scope
获取范围时container. scope
container. scope
,它实际上是container. getOrCreateScope()
container. getOrCreateScope()
,因此如果使用container. scope. close()
container. scope. close()
container. scope. close()
,container. scope
container. scope
仍然有效,因为它创建了一个新的container. scope
,这使为什么在关闭后仍可以使用它感到困惑
-
For StringQualifiedScope, only after the
close()
is called on the scope, can one recreate a scope with the same ScopeID i.e.ScopeNameID
, else it will crash. This doesn’t applies to TypeQualifiedScope though, as every time we get the scope from the object, it will provide the same scope instead of crashing.对于StringQualifiedScope,只有后
close()
被调用的范围,可以重新创建一个具有相同ScopeID即范围ScopeNameID
,否则会崩溃。 但是,这不适用于TypeQualifiedScope ,因为每次我们从对象获取范围时,它将提供相同的范围而不是崩溃。 -
Can we have
single
in thescope
? NO. Onlyscoped
is allowed.single
is error out during compile time. Essentiallyscoped
andsingle
is the same thing, but used in different place. I propose to them to use the same name as in我们可以在
scope
single
吗? 没有。 只允许scoped
。single
在编译时出错。 本质上,scoped
和single
是一回事,但是用在不同的地方。 我建议他们使用与
其他特性 (Other Features)
Other than the normal scope, there are other feature provided which is handy for scope usage. They are as below
除正常范围外,还提供了其他一些方便使用范围的功能。 他们如下
链接范围 (Link scope)
In order to get some dependencies
of another scope from one scope, we could link them together.
为了从一个作用域中获得另一个作用域的某些dependencies
,我们可以将它们链接在一起。
E.g. the below diagram showing we link scope_a
with scope_b
as well as scope_a
link with scope_a
(we could link just one of them, but I’m showing they could link bidirectionally just to demonstrate that’s possible)
例如,下面示出图我们链接scope_a
与scope_b
以及scope_a
与链路scope_a
(我们可以只链接其中之一,但是我正在展示它们可以双向链接只是为了证明这是可能的)
scopeA.linkTo(scopeB)
scopeB.linkTo(scopeA)
With that, we could
这样,我们可以
val somethingInScopeB = scopeA.get<SomeDependencyInScopeB>()
val somethingInScopeA = scopeB.get<SomeDependencyInScopeA>()
传递参数 (Passing parameters)
There are cases where we want some of our dependencies getting some specific parameter e.g. from which Scope it should gets it’s dependencies, or the qualified name.
在某些情况下,我们希望某些依赖项获取某些特定参数,例如,应该从哪个Scope获取依赖项或限定名称。
From the diagram below, we could send the parameter A, B and C, which is then provided to the dependency generator function.
从下图中,我们可以发送参数A,B和C,然后将其提供给依赖项生成器函数。
Below is an example, we have an Environment
which is dependent on Dependency
. So we have to tell Environment
which scope should it get its Dependency
from.
下面是一个示例,我们有一个依赖于Dependency
的Environment
。 因此,我们必须告诉Environment
应该从哪个范围获得Dependency
。
class Environment(val dependency: Dependency)val mainKoinModule =module {single(qualifier = named("single")) { Dependency() }factory(qualifier = named("factory")) { Dependency() }scope(named("AScopeName")) {scoped(qualifier = named("scopedName")) { Dependency() }factory(qualifier = named("factoryName")) { Dependency() }
}scope<Container> {scoped(qualifier = named("scopedContainer")) { Dependency() }factory(qualifier = named("factoryContainer")) {Dependency()}
}
factory { (scopeId: ScopeID, name: String) ->
Environment(getScope(scopeId).get(qualifier = named(name)))
}
}
In order to do that, we could pass the parameter over, upon what we set above
为了做到这一点,我们可以在上面设置的值上传递参数
(scopeId: ScopeID, name: String)
So the way to get it is
所以得到它的方法是
val scopeID = someScope.id // ScopeID is actually a String
val qualifiedName = "someQualifierval environment = get<Environment>(
parameters = { parametersOf(scopeID, qualifierName) })
Android生命周期范围 (Android Lifecycle Scope)
This is specifically to Android. (Note, you’ll need to get the koin-android library instead of koin-core library).
这是专门针对Android的。 (请注意,您需要获取koin-android库而不是koin-core库)。
You could define your module using your Activity as the Type.
您可以使用“活动”作为“类型”来定义模块。
val androidModule = module {
scope<MyActivity> {
scoped { Dependency() }
}
}
To get your dependencies, you’ll just need to use the lifecycleScope
instead of normal scope as shown above.
为了获得依赖关系,您只需要使用lifecycleScope
而不是上面显示的常规范围即可。
class MyActivity : AppCompatActivity() {
// inject Dependency instance from current scope
val dependency : Dependency by lifecycleScope.inject()
What does this provides further?
这进一步提供了什么?
Tracing down the code, it’s actually automatically close upon ON_DESTROY, hence you don’t need to close it. That’s it.
跟踪代码,实际上它会在ON_DESTROY时自动关闭,因此您无需关闭它。 而已。
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
if (event == Lifecycle.Event.ON_DESTROY) {
scope._koin._logger.debug("$target received ON_DESTROY")
scope.close()
}
}
}
I have coded the example below. Check out if you have trouble working on them
我已将以下示例编码。 检查您在处理它们时是否遇到麻烦
Thanks for reading. You can check out my other topics here.
谢谢阅读。 您可以在此处查看我的其他主题。
Follow me on medium, Twitter, Facebook or Reddit for little tips and learning on mobile development etc related topics. ~Elye~
在媒体 , Twitter , Facebook或Reddit上关注我,获取有关移动开发等相关主题的小技巧和学习。 〜艾莉〜
翻译自: https://medium.com/mobile-app-development-publication/kotlin-koin-scope-illustrated-3bfa6c7ae98
史蒂夫科恩