android - 在 Kotlin 的 Injekt 库中,如何创建本地作用域,例如为每个 Android Activity 创建本地作用域?

标签 android dependency-injection kotlin kohesive-injekt

在 Github 上的 Injekt 文档中,它说范围存在,但不清楚如何使用它们为每个 Android Activity 创建本地范围,这些 Activity 拥有自己的工厂和实例,但也可以使用来自父范围。

Injekt 变量似乎是全局范围,我看到 InjektScopeInjektScopeMain 但没有如何使用它们的示例,或者它们如何链接到父范围。我看到的唯一方法是创建单独的 InjektScope 实例并调用它们,或者将 Injekt 作为全局范围调用。这可行,但很笨拙。

没有明显的链接、嵌套、委托(delegate)或继承方式。

是否支持,如果支持如何?

注意: 这个问题是作者有意编写和回答的 ( Self-Answered Questions ),因此常见 Injekt + Kotlin 主题的惯用答案都出现在 SO 中。也欢迎其他答案,还有其他风格的如何做到这一点!声明一下,我是 Injekt 库的作者。

最佳答案

更新: 这现已记录在 Injekt README

它是通过范围之间的委托(delegate)工厂来支持的。首先,一些背景知识:

Injekt 允许手动将实例范围限定到单独的 Injekt 注册表中。通过 Injekt 变量提供的全局注册表只是为您预先创建的一个范围。您还可以创建新的:

val myLocalScope: InjektScope = InjektScope(DefaultRegistrar())

这会形成一个独立的作用域,与全局或其他作用域没有关系。

但是,您可以通过在新作用域中创建工厂来链接作用域,将一些实例创建委托(delegate)给另一个作用域。例如,上面提到的 myLocalScope 可以将一些工厂委托(delegate)给 Injekt 全局范围:

// delegate some factories to global Injekt instance
myLocalScope.addSingletonFactory { Injekt.get<SomeSingletonClass>() }
myLocalScope.addFactory { Injekt.get<SomeMultiValueClass>() }

当委托(delegate)这样的工厂时,任何多值实例都不会被任何范围缓存,因为这些工厂在每次调用时都会创建新实例。对于单例和键控工厂,对象被缓存,并且对这些对象的引用将存在于其生命周期期间请求的任何实例的本地范围和委托(delegate)范围中。

您还可以独立使用多个作用域,而无需链接或委派。从本地范围获取一些实例,从全局范围获取其他实例。但您必须独立使用每个作用域,并小心不要在无意时意外使用 Injekt 全局变量。

如果您在本地作用域中需要通用工厂,则可以轻松创建 InjektScope 的后代,在其构造期间注册这些工厂。

class MyActivityScope: InjektScope(DefaultRegistrar()) {
    init {
        // override with local value
        addSingletonFactory { SomeSingletonClass() }
        // import other registrations from defined modules
        importModule(OtherModuleWithPrepackagedInjektions)
        // delegate to global scope:
        addSingletonFactory { Injekt.get<SomeOtherSingleton>() }
    }
}

// then in each place you want a local scope
val localScope = MyActivityScope()

// later use the scope
val singly: SomeSingletonClass = localScope.get()
val other: SomeOtherSingleton = localScope.get()

或者使用与 InjektMain 相同的模型创建 InjektScopedMain 的后代,覆盖函数 fun InjektRegistrar.registerInjectables() { ... } ,如果您希望与模块保持一致。例如:

class MyActivityModule: InjektScopedMain(InjektScope(DefaultRegistrar())) {
    override fun InjektRegistrar.registerInjectables() {
        // override with local value
        addSingletonFactory { NotLazy("Freddy") }
        // import other registrations from defined modules
        importModule(OtherModuleWithPrepackagedInjektions)
        // delegate to global scope:
        addSingletonFactory { Injekt.get<SomeOtherSingleton>() }
    }
}

// then in each place you want a local scope
val localScope = MyActivityModule().scope

并且您仍然可以使用委托(delegate)属性,只要在委托(delegate)中使用之前声明范围即可:

val myProp: SomeClass by localScope.injectValue()

您可以使用LocalScoped基类来拥有injectValue()injectLazy()的本地版本,以便在注入(inject)时更加方便成员(参见code for LocalScoped)。这样您的语法就可以保持一致(请参阅 example in tests )。

要清除本地作用域,请删除对该作用域的引用,它将被垃圾收集掉。没有明确的明确方法。

有关更高级、更自动的作用域链接/委托(delegate)/继承,请参阅Injekt Github Issue #31并对 future 可能的功能提供评论。

有关让其他类在注入(inject)到声明本地作用域的类中时继承相同本地作用域的用例,请参阅 Injekt Github Issue #32

关于android - 在 Kotlin 的 Injekt 库中,如何创建本地作用域,例如为每个 Android Activity 创建本地作用域?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35315548/

相关文章:

java - 使用 CDI 生产者会导致不明确的依赖项异常

android - 在协程中切换到 UI 上下文

kotlin - 当 contains() 接受 E 时,Kotlin 的 Set 如何是协变的?

android - 实现持久数据队列以保证 Android 上的云同步

java - 当我运行应用程序时 android.support.v7.widget.CardView 不显示

c# - mvc - 重用服务行为

dependency-injection - 如何使用 caSTLe windsor wcf 设施注册我的所有服务

android - kotlin 中的重载解析歧义错误

android - 能否以编程方式为 ImageButton 生成聚焦图像?

android - eclipse v4.2.1 在 Windows 7 上非常慢