协程LifecycleScope源码解析

Posted yechaoa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了协程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

whenResumedlaunchWhenResumed执行时机一样,区别在于:

  • 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(this@LifecycleCoroutineScopeImpl)
             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(this@whenStateAtLeast, 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。
在父类中通过ReportFragmentActivityLifecycleCallbacks接口来派发当前生命周期状态,具体使用哪种派发方式要看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的使用,以及执行流程的具体分析。

最后

写作不易,如果对你有一丢丢帮助或启发,感谢点赞支持 ^ - ^

以上是关于协程LifecycleScope源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin之协程coroutine lifecycleScope 和 viewModelScope源码

关于协程,你知道LifecycleScope吗,超详细解释给你听!

lifecycleScope 和viewModelScope

关于协程,你知道LifecycleScope吗,超详细解释给你听,真的太香了

深入理解Kotlin协程CoroutineScope.launch源码追踪扒皮

使用 viewLifecycleOwner 的生命周期范围从 Fragment 启动协程