当前位置:   article > 正文

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

lifecyclescope

前言
使用协程,相信很多同学已经信手拈来了,但是也有很多同学是不知道LifecycleScope的。

LifecycleScope,顾名思义,具有生命周期的协程。
它是LifecycleOwner生命周期所有者的扩展属性,与LifecycleOwner生命周期绑定,并会在LifecycleOwner生命周期destroyed的时候取消掉。

推荐理由:

自动取消,不会造成内存泄漏,可以替代MainScope。
可以基于指定的生命周期执行。
后面会重点介绍LifecycleScope是怎么做到的。

使用
引入
协程:

  1. implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
  2. implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'


Lifecycle:

  1. implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1")


LifecycleScope虽然是协程,但属于Lifecycle中的扩展属性。

示例:
lifecycleScope默认主线程,可以通过withContext来指定线程。

  1. lifecycleScope.launch {
  2.     // do
  3.     withContext(Dispatchers.IO) {
  4.         // do
  5.     }
  6. }
  7. // or
  8. lifecycleScope.launch(Dispatchers.IO){
  9.     // do
  10. }
  11. // or
  12. lifecycleScope.launch {
  13.     whenResumed {
  14.         // do
  15.     }
  16. }
  17. // or
  18. lifecycleScope.launchWhenResumed {
  19.     // do
  20. }

whenResumed和launchWhenResumed执行时机一样,区别在于:

whenResumed 可以有返回结果
launchWhenResumed 返回的是Job对象
共有三个对应生命周期的扩展函数:

whenCreated
whenStarted
whenResumed
使用非常简单,关键在于它是怎么保证不会内存泄露的,又是怎么知道在某个生命周期的时候去执行协程的?

源码分析
1、如何保证不会内存泄漏的
先看lifecycleScope源码:

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

继承自LifecycleCoroutineScope,而LifecycleCoroutineScope是CoroutineScope的子类(协程层级关系)。

get()返回lifecycle.coroutineScope

这里有一个源码小技巧,当继承对象与返回对象不一致时,那么返回对象多半为继承对象的子类。

继续看lifecycle.coroutineScope:

  1. public val Lifecycle.coroutineScope: LifecycleCoroutineScope
  2.     get() {
  3.         while (true) {
  4.             val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
  5.             if (existing != null) {
  6.                 return existing
  7.             }
  8.             val newScope = LifecycleCoroutineScopeImpl(
  9.                 this,
  10.                 SupervisorJob() + Dispatchers.Main.immediate
  11.             )
  12.             if (mInternalScopeRef.compareAndSet(null, newScope)) {
  13.                 newScope.register()
  14.                 return newScope
  15.             }
  16.         }
  17.     }

果不其然,也是继承LifecycleCoroutineScope。
关键在于,通过LifecycleCoroutineScopeImpl创建了协程,默认主线程,随后又调用了newScope.register()

继续看LifecycleCoroutineScopeImpl:

  1. internal class LifecycleCoroutineScopeImpl(
  2.     override val lifecycle: Lifecycle,
  3.     override val coroutineContext: CoroutineContext
  4. ) : LifecycleCoroutineScope(), LifecycleEventObserver {
  5.     //...
  6.     fun register() {
  7.         launch(Dispatchers.Main.immediate) {
  8.             if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
  9.                 lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
  10.             } else {
  11.                 coroutineContext.cancel()
  12.             }
  13.         }
  14.     }
  15.     override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
  16.         if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
  17.             lifecycle.removeObserver(this)
  18.             coroutineContext.cancel()
  19.         }
  20.     }
  21. }

在register()方法中添加了LifecycleEventObserver接口的监听,LifecycleEventObserver会在onStateChanged方法中派发当前生命周期,关键来了,在onStateChanged回调中,判断当前生命周期是destroyed的时候,移除监听,并取消协程。

至此,相信大部分同学都明白了为什么不会造成内存泄露了,因为在页面destroyed的时候,协程会取消,并不会继续执行,而MainScope是需要手动取消的,否则会有内存泄露的风险。

插曲,我们进一步思考,在其他的开发场景中,也可以学习源码通过添加LifecycleEventObserver监听的方式,做回收清理操作,来避免内存泄漏。

author:yechaoa

2、如何知道在某个生命周期去执行协程
以lifecycleScope.launchWhenResumed为例,一探究竟。

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

调用whenResumed:

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

接着调用whenStateAtLeast,并传入一个具体生命周期状态作为标识。

继续看whenStateAtLeast:

  1. suspend fun <T> Lifecycle.whenStateAtLeast(
  2.     minState: Lifecycle.State,
  3.     block: suspend CoroutineScope.() -> T
  4. ) = withContext(Dispatchers.Main.immediate) {
  5.     val job = coroutineContext[Job] ?: error("when[State] methods should have a parent job")
  6.     val dispatcher = PausingDispatcher()
  7.     val controller =
  8.         LifecycleController(this@whenStateAtLeast, minState, dispatcher.dispatchQueue, job)
  9.     try {
  10.         withContext(dispatcher, block)
  11.     } finally {
  12.         controller.finish()
  13.     }
  14. }

这里创建了LifecycleController,并向下传入接收的具体状态,同时还有一个调度队列dispatcher.dispatchQueue。

接着看LifecycleController:

  1. @MainThread
  2. internal class LifecycleController(
  3.     private val lifecycle: Lifecycle,
  4.     private val minState: Lifecycle.State,
  5.     private val dispatchQueue: DispatchQueue,
  6.     parentJob: Job
  7. ) {
  8.     private val observer = LifecycleEventObserver { source, _ ->
  9.         if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
  10.             // cancel job before resuming remaining coroutines so that they run in cancelled
  11.             // state
  12.             handleDestroy(parentJob)
  13.         } else if (source.lifecycle.currentState < minState) {
  14.             dispatchQueue.pause()
  15.         } else {
  16.             dispatchQueue.resume()
  17.         }
  18.     }
  19.     init {
  20.         // If Lifecycle is already destroyed (e.g. developer leaked the lifecycle), we won't get
  21.         // an event callback so we need to check for it before registering
  22.         // see: b/128749497 for details.
  23.         if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
  24.             handleDestroy(parentJob)
  25.         } else {
  26.             lifecycle.addObserver(observer)
  27.         }
  28.     }
  29.     //...
  30. }

在init初始化的时候,添加LifecycleEventObserver监听(又是一个使用案例,不过这里用的是lambda写法)。

在回调中,对生命周期进行了判断,当大于当前状态的时候,也就是生命周期执行到当前状态的时候,会调用dispatchQueue.resume()执行队列,也就是协程开始执行。

  1. dispatchQueue.resume:
  2.     @MainThread
  3.     fun resume() {
  4.         if (!paused) {
  5.             return
  6.         }
  7.         check(!finished) {
  8.             "Cannot resume a finished dispatcher"
  9.         }
  10.         paused = false
  11.         drainQueue()
  12.     }
  13.     
  14.     //...
  15.     @MainThread
  16.     fun drainQueue() {
  17.         if (isDraining) {
  18.             // Block re-entrant calls to avoid deep stacks
  19.             return
  20.         }
  21.         try {
  22.             isDraining = true
  23.             while (queue.isNotEmpty()) {
  24.                 if (!canRun()) {
  25.                     break
  26.                 }
  27.                 queue.poll()?.run()
  28.             }
  29.         } finally {
  30.             isDraining = false
  31.         }
  32.     }

关于怎么获取到当前生命周期状态的,就涉及到Lifecycle相关的知识了,简而言之,不管是Activity还是Fragment,都是LifecycleOwner,其实是父类实现的,比如ComponentActivity。
在父类中通过ReportFragment或ActivityLifecycleCallbacks接口来派发当前生命周期状态,具体使用哪种派发方式要看Api等级是否在29(10.0)及以上,及 则后者。

验证分析
验证一下我们的分析是否正确。

代码简单测试:

  1. class MainActivity : AppCompatActivity() {
  2.     override fun onCreate(savedInstanceState: Bundle?) {
  3.         super.onCreate(savedInstanceState)
  4.         setContentView(R.layout.activity_main)
  5.         Log.i("tag","onCreate")
  6.         lifecycleScope.launchWhenResumed {
  7.             Log.i("tag","launchWhenResumed")
  8.         }
  9.     }
  10.     override fun onResume() {
  11.         super.onResume()
  12.         Log.i("tag","onResume")
  13.     }
  14. }

同时对源码进行debug。

  1.  I/tag: onCreate
  2.  I/tag: onResume
  3.  I/tag: launchWhenResumed

通过打印,并结合断点执行顺序来看,以上分析是完全正确的。

总结
我们再来总结一下lifecycleScope协程执行时机的流程。

调用lifecycleScope,返回lifecycle.coroutineScope;
在coroutineScope中通过LifecycleCoroutineScopeImpl创建了协程,并调用了register()方法添加了对生命周期的监听,这个监听其实是为了在生命周期destroyed的时候取消协程;
随后才是调用具体执行状态的代码,比如launchWhenResumed;
然后调用whenStateAtLeast,并传入协程具体要执行的状态,比如Lifecycle.State.RESUMED;
在whenStateAtLeast中创建了LifecycleController,并向下传入具体执行状态,和一个队列;
在LifecycleController初始化的时候,也添加了对生命周期的监听LifecycleEventObserver,在回调中,通过当前生命周期的状态与具体要执行状态的判断,来决定是否执行协程队列,满足条件,即执行。
以上,就是lifecycleScope的使用,以及执行流程的具体分析。


————————————————
版权声明:本文为CSDN博主「yechaoa」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yechaoa/article/details/118077968

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/495662
推荐阅读
相关标签
  

闽ICP备14008679号