【问题标题】:Kotlin coroutines delay do not work on IOS queue dispatcherKotlin 协程延迟不适用于 IOS 队列调度程序
【发布时间】:2021-06-25 01:07:00
【问题描述】:

我有一个 KMM 应用,并且有代码:

fun getWeather(callback: (WeatherInfo) -> Unit) {
        println("Start loading")
        GlobalScope.launch(ApplicationDispatcher) {
            while (true) {
                val response = httpClient.get<String>(API_URL) {
                    url.parameters.apply {
                        set("q", "Moscow")
                        set("units", "metric")
                        set("appid", weatherApiKey())
                    }
                    println(url.build())
                }
                val result = Json {
                    ignoreUnknownKeys = true
                }.decodeFromString<WeatherApiResponse>(response).main
                callback(result)

                // because ApplicationDispatcher on IOS do not support delay
                withContext(Dispatchers.Default) { delay(DELAY_TIME) }
            }
        }
    }

如果我将withContext(Dispatchers.Default) { delay(DELAY_TIME) } 替换为delay(DELAY_TIME) 执行将永远不会返回到while 循环,它只会进行一次迭代。

而 IOS 的 ApplicationDispatcher 看起来像:

internal actual val ApplicationDispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())

internal class NsQueueDispatcher(
    private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        dispatch_async(dispatchQueue) {
            block.run()
        }
    }
}

delay 源代码我可以猜到,DefaultDelay 应该被返回并且应该有类似的行为有/没有withContext(Dispatchers.Default)

/** Returns [Delay] implementation of the given context */
internal val CoroutineContext.delay: Delay get() = get(ContinuationInterceptor) as? Delay ?: DefaultDelay

谢谢!

附:我从ktor-samples 得到ApplicationDispatcher

【问题讨论】:

    标签: ios kotlin kotlin-coroutines kotlin-multiplatform kmm


    【解决方案1】:

    可能ApplicationDispatcher是老东西了,你不用再用了:

    CoroutineScope(Dispatchers.Default).launch {
    
    }
    

    MainScope().launch {
    
    }
    

    别忘了使用-native-mt 版本的协程,更多信息请看issue

    【讨论】:

    • 谢谢!对我来说,第一个变体会从 Android 抛出异常,因为我在回调中更改了 UI。但GlobalScope.launch(Dispatchers.Main) 适用于 Android 和 IOS
    • 您使用第一个变体在后台队列上运行,第二个变体用于主队列。我认为GlobalScope.launch(Dispatchers.Main) 应该相当于第二个。因此,如果您正在做一些巨大的计算,请在第一个内部运行这些计算,当您在第二个内部调用 ui 更新时,返回 UI 线程
    • 通常你应该避免使用 GlobalScope,因为你失去了结构化并发的好处。或者,您可以在 ViewModel 中保存对 MainScope() 的引用,或类似的 - kotlinlang.org/docs/coroutines-guide.html#additional-references
    • 我在structured-concurrency 中发现了很多有趣的东西,谢谢@enyciaa!
    猜你喜欢
    • 2019-01-15
    • 2021-06-27
    • 1970-01-01
    • 2011-10-28
    • 1970-01-01
    • 2013-05-29
    • 2020-09-15
    • 2018-04-20
    • 2015-10-31
    相关资源
    最近更新 更多