使用明確的job來控制協(xié)程生命周期

我們把關(guān)于contexts\childrenjobs的知識融合到一起,試想我們有一個(gè)具備生命周期的對象,但這個(gè)對象不是一個(gè)協(xié)程。例如,在開發(fā)Android應(yīng)用時(shí),為了執(zhí)行獲取網(wǎng)絡(luò)數(shù)據(jù)、執(zhí)行動(dòng)畫等異步操作,我們在activity的里開啟了各種協(xié)程。為了避免內(nèi)存泄漏,當(dāng)activity被銷毀時(shí),所有的這些協(xié)程必須取消。

通過創(chuàng)建一個(gè)Job的實(shí)例來控制我們協(xié)程的生命周期,把它綁定到activity的生命周期上。在activity被創(chuàng)建時(shí),使用工廠方法Job()創(chuàng)建一個(gè)job實(shí)例;當(dāng)activity被銷毀時(shí),它如下這樣被取消:

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

這個(gè)activity實(shí)現(xiàn)了CoroutineScope接口,我們只需要重寫CoroutineScope.coroutineContext屬性來指定協(xié)程在這個(gè)scope內(nèi)的執(zhí)行上下文環(huán)境context。那么在這個(gè)activity里啟動(dòng)協(xié)程就不需要再明確指定他們的執(zhí)行環(huán)境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!

相關(guān)概念

協(xié)程,是線程中的,也就是說一個(gè)線程中可能包含多個(gè)協(xié)程,協(xié)程與協(xié)程之間是可以嵌套的

  1. Scope

    用來給協(xié)程分組,在同一個(gè)CoroutineScope下創(chuàng)建的協(xié)程(如果沒有顯示指定其他Scope),默認(rèn)都是父子關(guān)系,這樣的好處在于cancel父協(xié)程后,所有的子協(xié)程都可以被一起cancel掉。

  2. Context

    當(dāng)前協(xié)程的上下文,用于在協(xié)程與協(xié)程之間參數(shù)傳遞。

    可以用于聲明當(dāng)前 協(xié)程 在哪一個(gè)線程中聲明,以及當(dāng)前 協(xié)程 被中斷后,在哪一個(gè)線程中恢復(fù)它

  3. Job

    代表了協(xié)程本身,協(xié)程不僅包含了上下文,其本身還是可執(zhí)行體。
    任務(wù),封裝了協(xié)程中需要執(zhí)行的代碼邏輯。Job 可以取消并且有簡單生命周期

  4. Dispatcher

    調(diào)度器,調(diào)度協(xié)程運(yùn)行在哪個(gè)線程上。決定協(xié)程所在的線程或線程池。它可以指定協(xié)程運(yùn)行于特定的一個(gè)線程、一個(gè)線程池或者不指定任何線程(這樣協(xié)程就會運(yùn)行于當(dāng)前線程)

  5. withcontext

    不會創(chuàng)建新的協(xié)程,在指定協(xié)程上運(yùn)行掛起代碼塊,并掛起該協(xié)程直至代碼塊運(yùn)行完成

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容