Kotlin之协程coroutine lifecycleScope 和 viewModelScope源码
Posted 梁景杰Android
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin之协程coroutine lifecycleScope 和 viewModelScope源码相关的知识,希望对你有一定的参考价值。
上一篇文章 Kotlin之协程coroutine使用(1) 文末介绍了Activity,Fragment 和 ViewModelScope
对应的自动绑定生命周期协程开启方式, lifecycleScope 和 viewModelScope
这篇文章,就解剖一下,为什么这两货可以绑定生命周期,不需要用户自己去绑定取消?
=========================================================================
Activity 和 Fragment 对应的 lifecycleScope
=========================================================================
先看一下 Activity 开启方式
class CoroutinesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_coroutines)
// 通过 lifecycleScope 开启
lifecycleScope.launch(Dispatchers.IO) {
}
}
}
再看一下 Fragment 开启方式
class CoroutinesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// viewLifecycleOwner.lifecycleScope
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
}
}
}
点进去各自的 lifecycleScope 看一下,其实都是 androidx.lifecycle:lifecycle-runtime-ktx 包下的LifecycleOwner 对应的 lifecycleScope ,
可以看到是通过 lifeCycle 获得 coroutineScope,那究竟 lifeCycle 是怎么获得 coroutineScope,又是怎么将 coroutineScope 和生命周期绑定的,我们进去看一下
/**
* [CoroutineScope] tied to this [Lifecycle].
*
* This scope will be cancelled when the [Lifecycle] is destroyed.
*
* This scope is bound to
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
*/
public val Lifecycle.coroutineScope: LifecycleCoroutineScope
get() {
while (true) {
//注释1 先判断是否已经有 coroutineScope ,有则直接返回
val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
if (existing != null) {
return existing
}
//注释2 没有创建一个 coroutineScope
val newScope = LifecycleCoroutineScopeImpl(
this,
SupervisorJob() + Dispatchers.Main.immediate
)
//注释3 保存好 coroutineScope
if (mInternalScopeRef.compareAndSet(null, newScope)) {
//注释4 注册生命周期回调,绑定生命周期
newScope.register()
return newScope
}
}
}
//省略部分带码...
internal class LifecycleCoroutineScopeImpl(
override val lifecycle: Lifecycle,
override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
init {
// in case we are initialized on a non-main thread, make a best effort check before
// we return the scope. This is not sync but if developer is launching on a non-main
// dispatcher, they cannot be 100% sure anyways.
if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
coroutineContext.cancel()
}
}
fun register() {
launch(Dispatchers.Main.immediate) {
if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
//注释4 注册生命周期回调,绑定生命周期
lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
} else {
coroutineContext.cancel()
}
}
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
//注释5 当生命周期是 destroy 时,取消生命周期回调监听,取消协程
lifecycle.removeObserver(this)
coroutineContext.cancel()
}
}
}
上面源码添加注释1.2.3.4.5 已经很明显了,整个流程是:
1:Activity 和 Fragment 获得的 都是通过 lifecycleScope 通过 LifecycleOwner 获得
2:这个 coroutineScope 是通过 LifecycleCoroutineScopeImpl 封装,这个 LifecycleCoroutineScopeImpl 同时实现了 LifecycleEventObserver 和 CoroutineScope 接口。
3:所以(通过CoroutineScope )创建协程时,(通过LifecycleEventObserver )监听生命周期,当生命周期跑到destory时,取消监听并取消协程。
=========================================================================
ViewModel 对应的 viewModelScope
=========================================================================
看下 ViewModel 怎么开启,viewModelScope 是 ViewModel 自有属性,直接调用即可
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class MyViewModel : ViewModel() {
fun test() {
// 开启协程
viewModelScope.launch {
}
}
}
看下 viewModelScope 是怎么实现的
package androidx.lifecycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import java.io.Closeable
import kotlin.coroutines.CoroutineContext
private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"
/**
* [CoroutineScope] tied to this [ViewModel].
* This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
*
* This scope is bound to
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
*/
val ViewModel.viewModelScope: CoroutineScope
get() {
/*
* 注释1 每个ViewModel持有一个CoroutineScope
* 初次获取创建并保存,保存通过Map 保存
* 再次获取 直接返回
* getTag() 和 setTagIfAbsent() 都是通过Map读写,有兴趣的可以进去细看
*/
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
}
/**
*注释 2
*CloseableCoroutineScope 实现 CoroutineScope 和 Closeable
*
*CoroutineScope 实现协程功能
*Closeable 实现关闭/取消协程功能
*
*/
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
//注释3: 取消协程
coroutineContext.cancel()
}
}
看注释1.2.3.......知道ViewModel 怎么开启保存协程了,但是注释 3是取消协程的,到底什么时候调用的。要想知道什么时候调用,就要进入上面注释1,忽略的两个方法 getTag() 和 setTagIfAbsent() 这个两方法对应有一个 叫:mBagOfTags 的 Map,
看上面两个方法是看不出什么时候调用close()方法的,我们要看一下这个 mBagOfTags 还在其他什么地方/时候调用,在这个 ViewModel.java 里,我们很容易看到 mBagOfTags 有在 clear()方法里面调用
public abstract class ViewModel {
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
@MainThread
final void clear() {
mCleared = true;
// 注释1 清除 mBagOfTags 里面所有元素
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// 注释2 close每一个元素
closeWithRuntimeException(value);
}
}
}
onCleared();
}
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
// 注释3 close每一个元素
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
注释2 , 3 找到 close() 的地方。但是,我们是找到了关闭协程的地方,但是什么时候会调用 clear()
我们看一下 clear()引用的地方。
看到是通过 ViewModelStore 的 clear() 调用的。
发现通过Activity 和 Fragment 调用。笔者在跟其源码一步步找下去时,分别在Activity 和Fragment destory时会触发他们对应的方法,去清除释放他们对应ViewModel 的 mBagOfTags 持有 数据。
至于这一部分,笔者就不贴代码追踪,因为不是本文要学习的内容。有兴趣的同学可以去看一下。
在这里笔者提醒一下,看源码不要转牛角尖,看个大概逻辑思路即可,要猜一半看一半,千万不要让自己把每一步代码搞懂,防止深陷源码不能自拔。
【总结】
在这个再总结一下,ViewModel 会保存一个对应 ViewModelScope ,初次获取时会保存在其mBagOfTags 的Map里面,再次获取会从这个 mBagOfTags 取出。当 ViewModel 宿主体(Activity 或 Fragment )销毁时,Activity 或 Fragment会通过 ViewModelStore,把 ViewModel 的 mBagOfTags 持有数据全部清除释放掉,当然协程就是在这个时候释放取消掉。
以上是关于Kotlin之协程coroutine lifecycleScope 和 viewModelScope源码的主要内容,如果未能解决你的问题,请参考以下文章