美文网首页
使用明确的job来控制协程生命周期

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

作者: mihope | 来源:发表于2019-01-22 22:40 被阅读8次

    我们把关于contexts\childrenjobs的知识融合到一起,试想我们有一个具备生命周期的对象,但这个对象不是一个协程。例如,在开发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

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

    相关文章

      网友评论

          本文标题:使用明确的job来控制协程生命周期

          本文链接:https://www.haomeiwen.com/subject/ffkdjqtx.html