天天看點

關于協程,你知道LifecycleScope嗎,超詳細解釋給你聽!

前言

使用協程,相信很多同學已經信手拈來了,但是也有很多同學是不知道

LifecycleScope

的。

LifecycleScope

,顧名思義,具有生命周期的協程。

它是

LifecycleOwner

生命周期所有者的擴充屬性,與LifecycleOwner生命周期綁定,并會在LifecycleOwner生命周期

destroyed

的時候取消掉。

推薦理由:

  • 自動取消,不會造成

    記憶體洩漏

    ,可以替代MainScope。
  • 可以基于指定的

    生命周期

    執行。
後面會重點介紹LifecycleScope是怎麼做到的。

使用

引入

  • 協程:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
           
  • Lifecycle:
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1")
           
LifecycleScope雖然是協程,但屬于Lifecycle中的

擴充屬性

示例:

lifecycleScope預設

主線程

,可以通過

withContext

來指定線程。
lifecycleScope.launch {
    // do
    withContext(Dispatchers.IO) {
        // do
    }
}

// or

lifecycleScope.launch(Dispatchers.IO){
    // do
}

// or

lifecycleScope.launch {
    whenResumed {
        // do
    }
}

// or

lifecycleScope.launchWhenResumed {
    // do
}
           

whenResumed

launchWhenResumed

執行時機一樣,差別在于:

  • whenResumed 可以有傳回結果
  • launchWhenResumed 傳回的是Job對象

共有三個對應生命周期的擴充函數:

  • whenCreated
  • whenStarted
  • whenResumed

使用非常簡單,關鍵在于它是怎麼保證不會記憶體洩露的,又是怎麼知道在某個生命周期的時候去執行協程的?

源碼分析

1、如何保證不會記憶體洩漏的

先看

lifecycleScope

源碼:

val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope
           

繼承自

LifecycleCoroutineScope

,而LifecycleCoroutineScope是

CoroutineScope

的子類(協程層級關系)。

get()傳回

lifecycle.coroutineScope

這裡有一個源碼小技巧,當繼承對象與傳回對象不一緻時,那麼傳回對象多半為繼承對象的子類。

繼續看lifecycle.coroutineScope:

public val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            val newScope = LifecycleCoroutineScopeImpl(
                this,
                SupervisorJob() + Dispatchers.Main.immediate
            )
            if (mInternalScopeRef.compareAndSet(null, newScope)) {
                newScope.register()
                return newScope
            }
        }
    }
           

果不其然,也是繼承LifecycleCoroutineScope。

關鍵在于,通過

LifecycleCoroutineScopeImpl

建立了協程,預設

主線程

,随後又調用了newScope.register()

繼續看LifecycleCoroutineScopeImpl:

internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
    //...

    fun register() {
        launch(Dispatchers.Main.immediate) {
            if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
                lifecycle.addObserver([email protected])
            } else {
                coroutineContext.cancel()
            }
        }
    }

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }
}
           

register()

方法中添加了LifecycleEventObserver接口的監聽,LifecycleEventObserver會在

onStateChanged

方法中派發目前生命周期,關鍵來了,在onStateChanged回調中,判斷目前生命周期是

destroyed

的時候,移除監聽,并

取消協程

至此,相信大部分同學都明白了為什麼

不會造成記憶體洩露

了,因為在頁面destroyed的時候,協程會取消,并不會繼續執行,而

MainScope

是需要手動取消的,否則會有記憶體洩露的風險。

插曲,我們進一步思考,在其他的開發場景中,也可以學習源碼通過添加LifecycleEventObserver監聽的方式,做回收清理操作,來避免記憶體洩漏。
author:yechaoa

2、如何知道在某個生命周期去執行協程

lifecycleScope.launchWhenResumed

為例,一探究竟。

fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {
    lifecycle.whenResumed(block)
}
           

調用

whenResumed

suspend fun <T> Lifecycle.whenResumed(block: suspend CoroutineScope.() -> T): T {
    return whenStateAtLeast(Lifecycle.State.RESUMED, block)
}
           

接着調用

whenStateAtLeast

,并傳入一個具體生命周期狀态作為

辨別

繼續看whenStateAtLeast:

suspend fun <T> Lifecycle.whenStateAtLeast(
    minState: Lifecycle.State,
    block: suspend CoroutineScope.() -> T
) = withContext(Dispatchers.Main.immediate) {
    val job = coroutineContext[Job] ?: error("when[State] methods should have a parent job")
    val dispatcher = PausingDispatcher()
    val controller =
        LifecycleController([email protected], minState, dispatcher.dispatchQueue, job)
    try {
        withContext(dispatcher, block)
    } finally {
        controller.finish()
    }
}
           

這裡建立了

LifecycleController

,并向下傳入接收的具體狀态,同時還有一個排程隊列dispatcher.dispatchQueue。

接着看LifecycleController:

@MainThread
internal class LifecycleController(
    private val lifecycle: Lifecycle,
    private val minState: Lifecycle.State,
    private val dispatchQueue: DispatchQueue,
    parentJob: Job
) {
    private val observer = LifecycleEventObserver { source, _ ->
        if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
            // cancel job before resuming remaining coroutines so that they run in cancelled
            // state
            handleDestroy(parentJob)
        } else if (source.lifecycle.currentState < minState) {
            dispatchQueue.pause()
        } else {
            dispatchQueue.resume()
        }
    }

    init {
        // If Lifecycle is already destroyed (e.g. developer leaked the lifecycle), we won't get
        // an event callback so we need to check for it before registering
        // see: b/128749497 for details.
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            handleDestroy(parentJob)
        } else {
            lifecycle.addObserver(observer)
        }
    }
    //...
}
           

init

初始化的時候,添加LifecycleEventObserver監聽(又是一個使用案例,不過這裡用的是lambda寫法)。

在回調中,對生命周期進行了判斷,當大于目前狀态的時候,也就是生命周期執行到目前狀态的時候,會調用

dispatchQueue.resume()

執行隊列,也就是協程

開始執行

dispatchQueue.resume:

@MainThread
    fun resume() {
        if (!paused) {
            return
        }
        check(!finished) {
            "Cannot resume a finished dispatcher"
        }
        paused = false
        drainQueue()
    }
    
    //...

    @MainThread
    fun drainQueue() {
        if (isDraining) {
            // Block re-entrant calls to avoid deep stacks
            return
        }
        try {
            isDraining = true
            while (queue.isNotEmpty()) {
                if (!canRun()) {
                    break
                }
                queue.poll()?.run()
            }
        } finally {
            isDraining = false
        }
    }
           

關于怎麼擷取到目前生命周期狀态的,就涉及到

Lifecycle

相關的知識了,簡而言之,不管是

Activity

還是

Fragment

,都是

LifecycleOwner

,其實是父類實作的,比如ComponentActivity。

在父類中通過

ReportFragment

ActivityLifecycleCallbacks

接口來派發目前生命周期狀态,具體使用哪種派發方式要看

Api

等級是否在29(10.0)及以上,及 則後者。

驗證分析

驗證一下我們的分析是否正确。

代碼簡單測試:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.i("tag","onCreate")

        lifecycleScope.launchWhenResumed {
            Log.i("tag","launchWhenResumed")
        }
    }

    override fun onResume() {
        super.onResume()
        Log.i("tag","onResume")
    }
}
           

同時對源碼進行

debug

I/tag: onCreate
 I/tag: onResume
 I/tag: launchWhenResumed
           

通過列印,并結合斷點執行順序來看,以上分析是完全

正确

的。

總結

我們再來總結一下

lifecycleScope

協程執行時機的流程。

  1. 調用lifecycleScope,傳回lifecycle.coroutineScope;
  2. 在coroutineScope中通過LifecycleCoroutineScopeImpl建立了協程,并調用了register()方法添加了對生命周期的監聽,這個監聽其實是為了在生命周期destroyed的時候取消協程;
  3. 随後才是調用具體執行狀态的代碼,比如launchWhenResumed;
  4. 然後調用whenStateAtLeast,并傳入協程具體要執行的狀态,比如Lifecycle.State.RESUMED;
  5. 在whenStateAtLeast中建立了LifecycleController,并向下傳入具體執行狀态,和一個隊列;
  6. 在LifecycleController初始化的時候,也添加了對生命周期的監聽LifecycleEventObserver,在回調中,通過目前生命周期的狀态與具體要執行狀态的判斷,來決定是否執行協程隊列,滿足條件,即執行。

以上,就是

lifecycleScope

的使用,以及執行流程的具體分析。

最後

寫作不易,如果對你有一丢丢幫助或啟發,感謝

點贊

支援 ^ - ^