lifecycleScope 和viewModelScope
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lifecycleScope 和viewModelScope相关的知识,希望对你有一定的参考价值。
参考技术A 作用域CoroutineScope绑定到LifecycleOwner的生命周期,销毁生命周期的时候,取消此作用域。作用域的协程也会被取消。并且我们知道LifecycleOwner的生命周期可以和Activity绑定,因此也就是间接的将CoroutineScope和Activity的生命周期绑定。当Activity被销毁的时候,取消此作用域作用域CoroutineScope绑定到ViewModel,ViewModel被清除的时候,取消此作用域。
协程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(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。
在父类中通过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
的使用,以及执行流程的具体分析。
最后
写作不易,如果对你有一丢丢帮助或启发,感谢点赞
支持 ^ - ^
以上是关于lifecycleScope 和viewModelScope的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin之协程coroutine lifecycleScope 和 viewModelScope源码
关于协程,你知道LifecycleScope吗,超详细解释给你听!