Kotlin:为什么 job.invokeOnCompletion() 不在主线程上阻塞 运行?

Kotlin: Why isn't job.invokeOnCompletion() block running on main thread?

在我的 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() 中抛出以下异常:

debug: Only the original thread that created a view hierarchy can touch its views.

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

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

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

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

在协程的世界里,控制线程分配的是协程上下文,而显然您在创建作业时处于任何上下文之外。所以修复它的方法是从 invokeOnCompletion.

中显式 launch(Dispatchers.Main) 协程

关于你关于取消的问题,你可以用withContext(NonCancellable)包围你想要防止取消的部分代码。