1

最近几天,我一直在与这个例外作斗争。

我有一个具有以下依赖项的 kotlin 多平台项目:

  • 科特林=1.5.10
  • 科丁=7.6.0
  • ktor=1.6.0(内部使用 kotlin 协程 1.5.0-native-mt)

在尝试在本机中使用 httpClient 时,我一直在提到异常:

    at kotlin.Throwable#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Throwable.kt:23)
    at kotlin.Exception#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Exceptions.kt:23)
    at kotlin.RuntimeException#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/Exceptions.kt:34)
    at kotlin.native.concurrent.InvalidMutabilityException#<init>(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22)
    at <global>.ThrowInvalidMutabilityException(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:93)
    at <global>.MutationCheck(Unknown Source)
    at kotlin.collections.HashMap.<set-length>#internal(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:16)
    at kotlin.collections.HashMap#addKey(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:292)
    at kotlin.collections.HashMap#put(/Users/teamcity3/buildAgent/work/290aee0e088a1666/runtime/src/main/kotlin/kotlin/collections/HashMap.kt:68)
    at org.kodein.di.internal.DITreeImpl#find(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DITreeImpl.kt:132)
    at org.kodein.di.DITree#find$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DITree.kt:36)
    at org.kodein.di.internal.DIContainerImpl#factory(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DIContainerImpl.kt:158)
    at org.kodein.di.DIContainer#factory$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:32)
    at org.kodein.di.DIContainer#provider(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:76)
    at org.kodein.di.DIContainer#provider$default(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/DIContainer.kt:75)
    at org.kodein.di.internal.DirectDIBaseImpl#Instance(/Users/runner/work/Kodein-DI/Kodein-DI/kodein-di/src/commonMain/kotlin/org/kodein/di/internal/DirectDIImpl.kt:30)
    at InvalidMutabilitySampleTest.<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2#internal(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:26)
    at InvalidMutabilitySampleTest.$<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2$FUNCTION_REFERENCE$3.invoke#internal(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:25)
    at InvalidMutabilitySampleTest.$<init>$lambda-6$lambda-5$lambda-4$lambda-3$lambda-2$FUNCTION_REFERENCE$3.$<bridge-UNNN>invoke(/Users/r.juszczyk/StudioProjects/lmhu-multiplatform-app/MultiplatformApp/src/iosTest/kotlin/InvalidMutabilitySampleTest.kt:25)
    at io.ktor.client.HttpClientConfig.install$<anonymous>_1#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:69)
    at io.ktor.client.HttpClientConfig.$install$<anonymous>_1$FUNCTION_REFERENCE$17.invoke#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:65)
    at io.ktor.client.HttpClientConfig.$install$<anonymous>_1$FUNCTION_REFERENCE$17.$<bridge-UNNN>invoke(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:65)
    at io.ktor.client.features.json.JsonFeature.Feature#prepare(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-features/ktor-client-json/common/src/io/ktor/client/features/json/JsonFeature.kt:129)
    at io.ktor.client.HttpClientConfig.install$<anonymous>_1-2#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:77)
    at io.ktor.client.HttpClientConfig.$install$<anonymous>_1-2$FUNCTION_REFERENCE$18.invoke#internal(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:74)
    at io.ktor.client.HttpClientConfig.$install$<anonymous>_1-2$FUNCTION_REFERENCE$18.$<bridge-UNNN>invoke(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:74)
    at io.ktor.client.HttpClientConfig#install(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt:97)
    at io.ktor.client.HttpClient#<init>(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:172)
    at io.ktor.client.HttpClient#<init>(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:81)
    at io.ktor.client#HttpClient(/Users/administrator/Documents/agent/work/8d547b974a7be21f/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClient.kt:43)

我设法在测试中重现了该崩溃:

class InvalidMutabilitySampleTest {
    val di = DI {
        import(DI.Module("Some Module") {
            bind<Json>() with provider {
                Json {
                    prettyPrint = true
                    isLenient = true
                }
            }

            bind<HttpClient>() with provider {
                HttpClient{
                    install(JsonFeature) {
                        serializer = KotlinxSerializer(instance())
                    }
                }
            }
        })
    }

    val httpClient: HttpClient by di.instance()

    @Test
    fun invalidMutabilityTest() {
        println(httpClient)
    }

}

我还设法通过更改来修复它:

HttpClient{
   install(JsonFeature) {
      serializer = KotlinxSerializer(instance())
   }
}

到:

val json = instance<Json>()
HttpClient{
   install(JsonFeature) {
      serializer = KotlinxSerializer(json)
   }
}

然后我注意到 HttpClient 做了一件非常具体的事情——它在 init 块中冻结了自己。我设法用这个示例代码重现了它:

class FrozenConstructor(val block: ()->Unit) {
    init {
        freeze()
    }
}

class InvalidMutabilitySampleTest2 {
    val di = DI {
        import(DI.Module("Some Module") {
            bind<String>() with provider {
                "lolo"
            }

            bind<FrozenConstructor>() with provider {
                FrozenConstructor{
                    instance<String>()
                }
            }
        })
    }

    val frozenConstructor: FrozenConstructor by di.instance()

    @Test
    fun invalidMutabilityTest() {
        println(frozenConstructor)
    }
}

所以我的理论如下:

  1. kodein 尝试提供FrozenConstructor
  2. FrozenConstructor被创建并冻结自己,它的成员引用了 kodein
  3. kodein 尝试缓存提供的依赖项并尝试改变内部MutableMap冻结的内容,一切都崩溃了

有人可以确认这或多或少是正确的,如果不是,请纠正我?

你们还可以建议处理它的最佳方法,以及其他在那里等待的陷阱吗?

它是一个kodein错误吗?

如果我with provider不使用,为什么 kodein 必须将某些内容存储在可变映射中with singleton

4

2 回答 2

6

我想你已经很清楚了。Ktor 冻结自身及其所有配置,以确保它可以在 Kotlin/Native 中跨线程使用。Kodein 假设您只会从一个线程中触摸它,并且冻结是不安全的。(无论这是限制、错误还是设计缺陷,都可能需要解释。)

要解决这些问题,您需要避免意外捕获配置中this引用HttpClientKodein 内部的引用。HttpClient正如您所发现的,这样做的一个好方法是在 lambda 之外的辅助变量中从 DI 中获取实例。

于 2021-05-31T13:52:22.090 回答
3

你是对的。 FrozenConstructor冻结自身,因此冻结其block属性,作为 lambda,冻结其所有捕获,包括 DI 容器。

Kodein-DI 不支持冻结(因此不支持本机多线程)。自从 JB 宣布开发新的 GC 以来,已经决定不投资使其与原生多线程兼容。

于 2021-06-01T14:19:58.143 回答