我有一个启动协程的类,当销毁它们的Activity / Fragment被销毁时,它们可以被取消。但是,它没有按我预期的那样工作。当我在操作运行时从片段中退出时,协程取消不会生效,并且尝试访问不再存在的View
时会得到NPE。
open class CoroutineLauncher : CoroutineScope {
private val dispatcher: CoroutineDispatcher = Dispatchers.Main
private val supervisorJob = SupervisorJob()
override val coroutineContext: CoroutineContext
get() = dispatcher + supervisorJob
fun launch(action: suspend CoroutineScope.() -> Unit) = launch(block = action)
fun cancelCoroutines() {
supervisorJob.cancelChildren() //coroutineContext.cancelChildren() has same results
}
}
这是用法
class MyFragment : Fragment {
val launcher = CoroutineLauncher()
fun onSomeEvent() {
launcher.launch {
val result = someSuspendFunction()
if (!isActive) return
// CAUSES CRASH
myTextView.text = result.userText
}
}
override fun onDestroyView() {
super.onDestroyView()
launcher.cancelCoroutines()
}
}
我添加了日志行,以确保在崩溃之前都调用onDestroyView
和cancelCoroutines
。我觉得我缺少明显的东西,但是我正在做的事情似乎符合这里建议的食谱:https://proandroiddev.com/android-coroutine-recipes-33467a4302e9
有什么想法吗?
答案 0 :(得分:1)
好吧,我知道了。调用onSomeEvent
之后,正在调用cancelCoroutines
。由于我们在cancelChildren
而不是SupervisorJob
上调用cancel
,因此启动程序不会拒绝新的Jobs,并且由于取消操作已经发生,因此新的协程像正常一样运行并崩溃。我通过在调用launcher.launch
之前检查片段是否可见来解决此问题,并在该片段不可见时退出该方法。
也可以通过调用supervisorJob.cancel()
而不是supervisorJob.cancelChildren()
来解决此问题,尽管这样做还有其他我不希望的副作用