0

我有使用 KMM 构建的 MVVM 应用程序。ViewModel 包含几个用例。每个用例调用 Repository 的方法,Repository 调用 NetworkService 来执行 API 调用。用例、Repository 和 NetworkService 在共享模块中。我需要将所有 cookie 存储在从服务器接收到的共享首选项中。为此,我创建了自己的 cookie 存储并以这种方式安装它:

private val httpClient = HttpClient {
        install(HttpCookies) {
            storage = CookiesStorage(di)
        }
    }

这是cookies存储的代码:

class CookiesStorage(val di: DI) : CookiesStorage {

    private val cookiesStorageImpl = CookiesStorageImpl(di)

    override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
        cookiesStorageImpl.addCookie(requestUrl, cookie)
    }

    override fun close() {
    }

    override suspend fun get(requestUrl: Url) = cookiesStorageImpl.getCookies()
}

expect class CookiesStorageImpl(di: DI) {
    val di: DI
    fun addCookie(requestUrl: Url, cookie: Cookie)
    fun getCookies(): MutableList<Cookie>
}

由于在 iOS 和 Android 中存储键值数据的工作不同,我添加了 expect class CookiesStorageImpl。此类的 Android 实现现在如下:

actual class CookiesStorageImpl actual constructor(actual val di: DI) {

    private val cookieMap = mutableMapOf<String, String>()

    private val context: Context by di.instance()

    actual fun addCookie(requestUrl: Url, cookie: Cookie) {
        println("Set cookie name=${cookie.name}, value=${cookie.value}")
        cookieMap[cookie.name] = cookie.value
        context.getSharedPreferences("kmm_preferences", Context.MODE_PRIVATE)
    }

    actual fun getCookies() = mutableListOf<Cookie>().apply {
        cookieMap.forEach {
            this.add(Cookie(it.key, it.value))
        }
    }

}

如您所见,我在这里使用 di 初始化上下文。

这是android应用程序中的di图:

val appModule = DI.Module("app module") {
    import(viewModelModule)
    bind<Context>() with multiton { app: App ->
        app.applicationContext
    }
}

val viewModelModule = DI.Module("view model module") {
    import(useCaseModule)

    bind<ViewModelProvider.Factory>() with singleton {
        ViewModelFactory(instance())
    }
    bind<LoginViewModel>() with provider {
        LoginViewModel(instance())
    }
}

这是共享模块的DI:

val useCaseModule = DI.Module("use case module") {
    bind<LoginUseCase>() with singleton {
        LoginUseCase(di)
    }
}

因此,如您所见,我只是di从用例传递到CookiesStorageImpl. 但是当我运行应用程序时,访问上下文时出现以下错误:

org.kodein.di.DI$NotFoundException: No binding found for bind<Context> { ? { ? } }

因此,据我了解,问题在于 UseCase 对上下文一无所知,但我不明白如何将绑定传递给用例模块。提前感谢您的帮助!

UPD

这是我在 Application 类中添加图形的方式:

class App : Application(), DIAware {

    override val di by DI.lazy {
        import(appModule)
    }

}
4

1 回答 1

0

实际上,您正在绑定一个带有Appas 参数的 multiton:

bind<Context>() with multiton { app: App ->
    app.applicationContext
}

所以这意味着你需要通过一段App时间注入你的Context,比如:

private val context: Context by di.instance(arg = app)

由于我认为您不会有多个实例App,因此只有一个多实例(因此是单例),因此您也可以将您的应用程序作为 DI 图的一部分。

但在 KMM 项目中,上下文可能难以处理。我建议您查看我们关于 Android 上下文的视频 https://www.youtube.com/watch?v=yCLI_dIx660&t=137s

您还可以使用 KMP 库为您处理本地存储,例如多平台设置:https ://github.com/russhwolf/multiplatform-settings

于 2021-09-20T06:41:08.870 回答