简体   繁体   中英

Kotlin: Override parent job of coroutine

I am trying to migrate the following function to new Coroutine of Kotlin 1.3 :

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

But new GlobalScope.launch function doesn't have parent parameter. Documentation says:

The parent job is inherited from a CoroutineScope as well, but it can also be overridden with corresponding coroutineContext element.

But I don't know how to override parent job. I have implemented it like this for now but I am not sure if it will work the same way:

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
}

Can anyone help me?

UPDATE:

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

    init {
        owner.lifecycle.addObserver(this)
    }

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

Your second example is correct. You can use plus to add the job as a parent job for the new coroutine.

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

But the usage of GlobalScope is discouraged. It would be better to create an own CoroutineScope . Your CancelStrategy looks like a good candidate.

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()
    }
}

Now you can start your coroutines like this:

cancelStrategy.launch { ... }

What you want to have is called "structured concurrency" by aligning the lifecycle of your coroutines to some UI component.

Have a look at this documentation: https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md#structured-concurrency-lifecycle-and-coroutine-parent-child-hierarchy

Instead of using GlobalScope , you should consider implementing your own scope and maintaining a Job their, which you can cancel to cancel all your children as well.

Here's a simplified example:

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 {
          //...
       }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM