【问题标题】:Kotlin: Why isn't job.invokeOnCompletion() block running on main thread?Kotlin:为什么 job.invokeOnCompletion() 块不在主线程上运行?
【发布时间】:2021-06-29 09:39:15
【问题描述】:

在我的 Android 应用程序中,我的代码应该在自己的协程中定期运行并且应该可以取消。

为此,我有以下功能:

startJob():初始化作业,设置invokeOnCompletion()并在各自的范围内启动工作循环

private fun startJob() {
    if (::myJob.isInitialized && myJob.isActive) {
        return
    }
    myJob= Job()
    myJob.invokeOnCompletion {
        it?.message.let {
            var msg = it
            if (msg.isNullOrBlank()) {
                msg = "Job stopped. Reason unknown"
            }
            myJobCompleted(msg)
        }
    }
    CoroutineScope(Dispatchers.IO + myJob).launch {
        workloop()
    }
}

workloop():主工作循环。在每次迭代中设置延迟的循环中做一些工作:

private suspend fun workloop() {
    while (true) {
        // doing some stuff here
        delay(setDelayInMilliseconds)
    }
}

myJobCompleted:做一些最终确定。现在只需记录一条消息进行测试。

private fun myJobCompleted(msg: String) {
    try {
        mainActivityReference.logToGUI(msg)
    }
    catch (e:Exception){
        println("debug: " + e.message)
    }
}

运行此程序并调用myJob.Cancel() 将在myJobCompleted() 中引发以下异常:

调试:只有创建视图层次结构的原始线程才能接触其视图。

我很好奇为什么这段代码没有在主线程上运行,因为startJob()是从主线程调用的?

此外:是否有类似于在c# 中使用CancellationTokenSource 的选项,其中作业不会立即取消,但可以在while 循环的每次迭代中检查取消请求? 立即中断工作,不管它在做什么(尽管它几乎总是在等待取消延迟)对我来说似乎不是一个好主意。

【问题讨论】:

    标签: multithreading kotlin kotlin-coroutines


    【解决方案1】:

    Job.invokeOnCompletion 的约定不是在创建Job 的同一线程上运行。而且,这样的合同是不可能执行的。

    您不能指望任意一段代码在任意线程上运行,仅仅因为该线程上有一些较早的方法调用。 Android主GUI线程执行外部提交代码的能力比较特殊,涉及到顶层事件循环的存在。

    在协程的世界里,控制线程分配的是协程上下文,而显然你在创建作业时处于任何上下文之外。所以修复它的方法是在invokeOnCompletion 中显式地launch(Dispatchers.Main) 一个协程。

    关于取消的问题,您可以使用withContext(NonCancellable) 包围您想要保护免于取消的代码部分。

    【讨论】:

    • 只有一条评论:launch(Dispatchers.Main)CoroutineScope(Dispatchers.Main).launch {} 没有区别,是吗? Kotlin 中的整个命名约定对我来说仍然有点混乱......
    • 其实是有区别的:CoroutineScope(Main).launchGlobalScope.launch(Main) 是一样的坏习惯。在写launch(Main) 时,我故意省略了协程作用域的选择,因为本主题与此无关。
    猜你喜欢
    • 2020-10-18
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多