Kotlin: переопределить родительскую работу сопрограммы - PullRequest
0 голосов
/ 27 февраля 2019

Я пытаюсь перенести следующую функцию на новую Coroutine из Kotlin 1.3:

fun launchUI(strategy: CancelStrategy, block: suspend CoroutineScope.() -> Unit): Job {
    return launch(context = UI, parent = strategy.jobs, block = block)
}

Но новая функция GlobalScope.launch не имеет параметра parent.Документация гласит:

Родительское задание также наследуется от CoroutineScope, но его также можно переопределить с помощью соответствующего элемента coroutineContext.

Но я нене знаю, как переопределить родительскую работу.Я реализовал это сейчас так, но я не уверен, будет ли он работать так же:

fun launchUI(strategy: CancelStrategy, block: suspend CoroutineScope.() -> Unit): Job {
    val job = GlobalScope.launch(context = Dispatchers.Main, block = block)
    strategy.jobs.invokeOnCompletion {
        job.cancel()
    }
    return job
}

Кто-нибудь может мне помочь?

ОБНОВЛЕНИЕ:

class CancelStrategy(owner: LifecycleOwner, val jobs: Job) : LifecycleObserver {

    init {
        owner.lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        jobs.cancel()
    }
}

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

То, что вы хотите получить, называется «структурированным параллелизмом», поскольку жизненный цикл ваших сопрограмм соответствует некоторому компоненту пользовательского интерфейса.

Посмотрите на эту документацию: https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md#structured-concurrency-lifecycle-and-coroutine-parent-child-hierarchy

Вместо использования GlobalScope, вы должны рассмотреть возможность реализации собственной области видимости и ведения Job их, которые вы можете отменить, чтобы отменитьвсе ваши дети.

Вот упрощенный пример:

class Activity : CoroutineScope {
    lateinit var job: Job //tied to lifecycle of Activity
    fun create() {
        job = Job()
    }

    fun destroy() {
        //will cancel all child jobs as well
        println("cancel $job and all ${job.children.toList().size} children")
        job.cancel()
    }

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Default + job + CoroutineName("MyActivityContext")

    fun doSomething() {
       //we launch in the outer scope of Activity
       launch {
          //...
       }
    }
}
0 голосов
/ 27 февраля 2019

Ваш второй пример верен.Вы можете использовать plus, чтобы добавить работу в качестве родительской для новой сопрограммы.

fun launchUI(strategy: CancelStrategy, block: suspend CoroutineScope.() -> Unit): Job {
    return GlobalScope.launch(context = Dispatchers.Main + strategy.jobs, block = block)
}

Но использование GlobalScope не рекомендуется.Было бы лучше создать собственный CoroutineScope.Ваш CancelStrategy выглядит хорошим кандидатом.

class CancelStrategy(owner: LifecycleOwner, val jobs: Job) : LifecycleObserver, CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + jobs

    init {
        owner.lifecycle.addObserver(this)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        jobs.cancel()
    }
}

Теперь вы можете запустить свои сопрограммы следующим образом:

cancelStrategy.launch { ... }
...