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

In the Injekt library for Kotlin, how do I create local scopes such as for each Android activity?

在关于 Github 的 Injekt 文档中,它说范围存在,但不清楚我如何使用它们为每个 Android activity 创建本地范围,有自己的工厂和实例,但也可以使用父作用域中的一些。

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

没有明显的方法来 link、嵌套、委托或继承。

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

注: 此题是作者(Self-Answered Questions)特意写下并回答的,所以常用Injekt的地道答案+ Kotlin 主题出现在 SO 中。也欢迎其他答案,还有其他样式的方法!披露,我是 Injekt 库的作者。

更新: 现在记录在 Injekt README

范围之间的委托工厂支持它。首先,一些背景:

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

val myLocalScope: InjektScope = InjektScope(DefaultRegistrar())

这形成了一个与全局或其他无关的独立范围。

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

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

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

您也可以独立使用多个作用域,而无需 linking 或委派。从本地范围获取一些实例,从全局范围获取其他实例。但是您必须独立使用每个作用域,并小心不要意外使用 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

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

val myProp: SomeClass by localScope.injectValue()

您可以使用 LocalScoped 基础 class 来获得 injectValue()injectLazy() 的本地版本,以便在注入成员时更加方便(参见 code for LocalScoped). This way your syntax stays consistent (see example in tests).

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

有关更高级、更自动的作用域 linking / 委派 / 继承,请参阅 Injekt Github Issue #31 并提供有关此未来可能功能的评论。

关于让其他 classes 在注入到 class 声明局部作用域时继承相同的局部作用域的用例,请参阅 Injekt Github Issue #32