Android Дочерние сопрограммы не отменяются надлежащим образом (SupervisorJob) - PullRequest
0 голосов
/ 06 мая 2020

У меня есть класс, который запускает сопрограммы и позволяет их отменять, когда действие / фрагмент, из которого они вызываются, уничтожается. Однако это не работает, как я ожидал. Когда я выхожу из фрагмента во время выполнения операции, отмена сопрограммы не выполняется, и я получаю NPE при попытке доступа к View, которого больше не существует.

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 оба вызываются перед cra sh. Мне кажется, что мне не хватает чего-то очевидного, но то, что я делаю, похоже, соответствует предложенным здесь рецептам: https://proandroiddev.com/android-coroutine-recipes-33467a4302e9

Есть идеи?

1 Ответ

1 голос
/ 07 мая 2020

Хорошо, я разобрался. onSomeEvent был вызван после вызова cancelCoroutines. Поскольку мы вызываем cancelChildren в SupervisorJob вместо cancel, программа запуска не отклоняет новые задания, а поскольку отмена уже произошла, новая сопрограмма работает как обычно и дает сбой. Я исправил это, проверив, виден ли фрагмент перед вызовом launcher.launch и выйдя из метода, если фрагмент не виден.

Это также можно было исправить, вызвав supervisorJob.cancel() вместо supervisorJob.cancelChildren(), хотя у этого есть и другие побочные эффекты, которых я не хотел

...