Пользовательский метод получения контекста сопрограммы - PullRequest
0 голосов
/ 07 ноября 2018

Я исследую сопрограммы kotlin, связанные с Android, после выпуска 1.0.0.

Я нашел множество примеров создания ViewModel с областью действия (из компонентов arch) с созданием родительского задания и очисткой его в onCleared или действия с областью действия с созданием задания в onCreate и очисткой в ​​onDestroy (то же самое с onResume и onPause). В некоторых примерах я встречаю эту структуру кода (взято из официальных документов ):

override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

Вызывается ли этот пользовательский метод получения все время, когда мы запускаем новую сопрограмму из этой области? Разве это не плохо? Может быть, было бы лучше сохранить одно значение объема вместо того, чтобы каждый раз создавать новое?

[UPDATE]

Я принимаю это решение, если мы избавимся от lateinit задания и создадим его по-настоящему, но что если я захочу сделать что-то подобное (что мне делать? Это решение похоже на правильное или нет?):

class LifecycleCrScope: CoroutineScope, LifecycleObserver {

  private var _job: Job? = null
  override val coroutineContext: CoroutineContext
    get() = job() + Dispatchers.Main


  fun job() : Job {
    return _job ?: createJob().also { _job = it }
  }

  fun createJob(): Job = Job() // or another implementation

  @OnLifecycleEvent(ON_PAUSE)
  fun pause() {
    _job?.cancel()
    _job = null
  }
}

1 Ответ

0 голосов
/ 07 ноября 2018

Я думаю, что вы можете столкнуться с проблемами безопасности потоков, когда лениво добавляете Job, как в своем обновлении, потому что этот код оценивается из любого потока, который запускает сопрограмму. Исходный пример, с другой стороны, гарантирует, что задание настроено из основного потока, и это происходит до того, как другие потоки могут быть запущены в обычном действии Android.

Вы можете достичь чего-то похожего на первоначальный пример, создав целое CoroutineContext в начале области. Это также устраняет необходимость вычисления окончательного контекста для каждой запущенной сопрограммы. Например:

class LifecycleCrScope : CoroutineScope, LifecycleObserver {

    private var _ctx: CoroutineContext? = null
    override val coroutineContext: CoroutineContext
        get() = _ctx!! // throws when not within scope

    private fun startScope() {
        _ctx = Dispatchers.Main + Job()
    }

    private fun endScope() {
        _ctx!![Job]!!.cancel()  // throws only when misused, e.g. no startScope()
        _ctx = null
    }

    @OnLifecycleEvent(ON_RESUME) //  <-.
    fun resume() {               //    | Beware:
        startScope()             //    | symmetric but no scope
    }                            //    | during onCreate,
                                 //    | onStart, onStop, ...
    @OnLifecycleEvent(ON_PAUSE)  //  <-.
    fun pause() {
        endScope()
    }

}

В качестве альтернативы, если вам не нравится бросать

class LifecycleCrScope : CoroutineScope, LifecycleObserver {
    // initially cancelled. Jobs started outside scope will not execute silently
    private var _ctx: CoroutineContext = Dispatchers.Main + Job().apply { cancel() }
    ..
    private fun endScope() {
        _ctx[Job]!!.cancel()  // should never throw
    }
    ..
...