赞
踩
上一篇文章 Kotlin之协程coroutine使用(1) 文末介绍了Activity,Fragment 和 ViewModelScope
对应的自动绑定生命周期协程开启方式, lifecycleScope 和 viewModelScope
这篇文章,就解剖一下,为什么这两货可以绑定生命周期,不需要用户自己去绑定取消?
=========================================================================
=========================================================================
先看一下 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 自有属性,直接调用即可
- 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 持有数据全部清除释放掉,当然协程就是在这个时候释放取消掉。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。