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源码的主要内容,如果未能解决你的问题,请参考以下文章

快速上手 Kotlin 开发系列之协程的挂起

快速上手 Kotlin 开发系列之协程的挂起

Amphp之协程助手

Amphp之协程助手

并发之协程

kotlin - Coroutine 协程