前言
使用協程,相信很多同學已經信手拈來了,但是也有很多同學是不知道
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
協程執行時機的流程。
- 調用lifecycleScope,傳回lifecycle.coroutineScope;
- 在coroutineScope中通過LifecycleCoroutineScopeImpl建立了協程,并調用了register()方法添加了對生命周期的監聽,這個監聽其實是為了在生命周期destroyed的時候取消協程;
- 随後才是調用具體執行狀态的代碼,比如launchWhenResumed;
- 然後調用whenStateAtLeast,并傳入協程具體要執行的狀态,比如Lifecycle.State.RESUMED;
- 在whenStateAtLeast中建立了LifecycleController,并向下傳入具體執行狀态,和一個隊列;
- 在LifecycleController初始化的時候,也添加了對生命周期的監聽LifecycleEventObserver,在回調中,通過目前生命周期的狀态與具體要執行狀态的判斷,來決定是否執行協程隊列,滿足條件,即執行。
以上,就是
lifecycleScope
的使用,以及執行流程的具體分析。
最後
寫作不易,如果對你有一丢丢幫助或啟發,感謝
點贊
支援 ^ - ^