使用明确的job来控制协程生命周期 - Go语言中文社区

使用明确的job来控制协程生命周期


我们把关于contextschildrenjobs的知识融合到一起,试想我们有一个具备生命周期的对象,但这个对象不是一个协程。例如,在开发Android应用时,为了执行获取网络数据、执行动画等异步操作,我们在activity的里开启了各种协程。为了避免内存泄漏,当activity被销毁时,所有的这些协程必须取消。

通过创建一个Job的实例来控制我们协程的生命周期,把它绑定到activity的生命周期上。在activity被创建时,使用工厂方法Job()创建一个job实例;当activity被销毁时,它如下这样被取消:

class MyActivity : AppCompatActivity(), CoroutineScope {
    lateinit var job: Job
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        job = Job()
    }

    override fun onDestroy() {
        super.onDestroy()
        job.cancel() // Cancel job on activity destroy. After destroy all children jobs will be cancelled automatically
    }

    /*
     * Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
     * in this method throws an exception, then all nested coroutines are cancelled.
     */
    fun loadDataFromUI() = launch { // <- extension on current activity, launched in the main thread
           val ioData = async(Dispatchers.IO) { // <- extension on launch scope, launched in IO dispatcher
               // blocking I/O operation
           }
           // do something else concurrently with I/O
           val data = ioData.await() // wait for result of I/O
           draw(data) // can draw in the main thread
        }
 }

这个activity实现了CoroutineScope接口,我们只需要重写CoroutineScope.coroutineContext属性来指定协程在这个scope内的执行上下文环境context。那么在这个activity里启动协程就不需要再明确指定他们的执行环境context。

这里有一段示意代码

import kotlin.coroutines.*
import kotlinx.coroutines.*

class Activity : CoroutineScope {
    lateinit var job: Job

    fun create() {
        job = Job()
    }

    fun destroy() {
        job.cancel()
    }
    // to be continued ...

    // class Activity continues
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Default + job
    // to be continued ...

    // class Activity continues
    fun doSomething() {
        // launch ten coroutines for a demo, each working for a different time
        repeat(10) { i ->
            launch {
                delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc
                println("Coroutine $i is done")
            }
        }
    }
} // class Activity ends

fun main() = runBlocking<Unit> {
    val activity = Activity()
    activity.create() // create an activity
    activity.doSomething() // run test function
    println("Launched coroutines")
    delay(500L) // delay for half a second
    println("Destroying activity!")
    activity.destroy() // cancels all coroutines
    delay(1000) // visually confirm that they don't work    
}

//Launched coroutines
//Coroutine 0 is done
//Coroutine 1 is done
//Destroying activity!

相关概念

协程,是线程中的,也就是说一个线程中可能包含多个协程,协程与协程之间是可以嵌套的

  1. Scope

    用来给协程分组,在同一个CoroutineScope下创建的协程(如果没有显示指定其他Scope),默认都是父子关系,这样的好处在于cancel父协程后,所有的子协程都可以被一起cancel掉。

  2. Context

    当前协程的上下文,用于在协程与协程之间参数传递。

    可以用于声明当前 协程 在哪一个线程中声明,以及当前 协程 被中断后,在哪一个线程中恢复它

  3. Job

    代表了协程本身,协程不仅包含了上下文,其本身还是可执行体。
    任务,封装了协程中需要执行的代码逻辑。Job 可以取消并且有简单生命周期

  4. Dispatcher

    调度器,调度协程运行在哪个线程上。决定协程所在的线程或线程池。它可以指定协程运行于特定的一个线程、一个线程池或者不指定任何线程(这样协程就会运行于当前线程)

  5. withcontext

    不会创建新的协程,在指定协程上运行挂起代码块,并挂起该协程直至代码块运行完成

版权声明:本文来源简书,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://www.jianshu.com/p/40bbea76489f
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-01-12 13:00:40
  • 阅读 ( 900 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

推荐文章

猜你喜欢