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)
包围你想要防止取消的部分代码。
在我的 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)
包围你想要防止取消的部分代码。