Android子协同程序未正确取消(SupervisorJob)

时间:2020-05-06 17:00:09

标签: android kotlin kotlin-coroutines

我有一个启动协程的类,当销毁它们的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()
  }

}

我添加了日志行,以确保在崩溃之前都调用onDestroyViewcancelCoroutines。我觉得我缺少明显的东西,但是我正在做的事情似乎符合这里建议的食谱:https://proandroiddev.com/android-coroutine-recipes-33467a4302e9

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

好吧,我知道了。调用onSomeEvent之后,正在调用cancelCoroutines。由于我们在cancelChildren而不是SupervisorJob上调用cancel,因此启动程序不会拒绝新的Jobs,并且由于取消操作已经发生,因此新的协程像正常一样运行并崩溃。我通过在调用launcher.launch之前检查片段是否可见来解决此问题,并在该片段不可见时退出该方法。

也可以通过调用supervisorJob.cancel()而不是supervisorJob.cancelChildren()来解决此问题,尽管这样做还有其他我不希望的副作用