Coroutine lifecycleScope and viewModelScope source code of Kotlin (2)

Previous article Kotlin's coroutine use (1)   Activity, Fragment and ViewModelScope are introduced at the end of the article

The corresponding automatic binding life cycle coroutine opening method, lifecycleScope  and viewModelScope

In this article, let’s dissect it. Why can these two products be bound to the life cycle without the user needing to cancel the binding?

=========================================================================

The lifecycleScope  corresponding to Activity and Fragment 

=========================================================================

Let's first look at how to start the Activity

class CoroutinesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_coroutines)
        // 通过 lifecycleScope 开启
        lifecycleScope.launch(Dispatchers.IO) {
        }
    }
}

Look at the Fragment opening method again

class CoroutinesFragment : Fragment() {
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // viewLifecycleOwner.lifecycleScope
        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
        }
    }
}

Click into their respective lifecycleScopes to have a look. In fact, they are all lifecycleScopes corresponding to LifecycleOwner under the androidx.lifecycle:lifecycle-runtime-ktx package.

You can see that coroutineScope is obtained through lifeCycle, so how does lifeCycle obtain coroutineScope, and how does coroutineScope bind the life cycle? Let's go in and have a look

/**
 * [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()
        }
    }
}

It is already obvious to add the comment 1.2.3.4.5 to the source code above, and the whole process is:

1: Both Activity and Fragment are obtained through LifecycleScope through LifecycleOwner

2: This coroutineScope is encapsulated by LifecycleCoroutineScopeImpl, which implements both LifecycleEventObserver and CoroutineScope interfaces.

3: So ( through CoroutineScope ) when creating a coroutine, ( through LifecycleEventObserver ) monitor the life cycle, when the life cycle runs to destroy, cancel the monitoring and cancel the coroutine.

=========================================================================

viewModelScope  corresponding to ViewModel 

=========================================================================

Let's see how to open ViewModel, viewModelScope  is ViewModel's own property, just call it directly

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class MyViewModel : ViewModel() {

    fun test() {
        // 开启协程
        viewModelScope.launch {

        }
    }
}

See how viewModelScope   is implemented

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()
    }
}

See note 1.2.3....... Know how the ViewModel opens and saves the coroutine, but note 3 cancels the coroutine, when is it called. If you want to know when to call it, you need to enter Note 1 above, the two neglected methods getTag() and setTagIfAbsent() correspond to a Map called mBagOfTags ,

 Looking at the above two methods, we can’t tell when the close() method is called. We need to see where/when  this mBagOfTags is still called. In this ViewModel.java, we can easily see that mBagOfTags is in clear( ) method to call

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);
            }
        }
    }
}

Notes 2 and 3 are where close() is found. However, we have found the place to close the coroutine, but when will we call clear()

Let's look at where clear() references.

It is seen that it is called by the clear() of ViewModelStore.

Discovery is called by Activity and Fragment. When the author is looking for the source code step by step, they will trigger their corresponding methods in Activity and Fragment destroy respectively to clear and release the data held by mBagOfTags of their corresponding ViewModel.

As for this part, the author will not post code tracking, because it is not the content of this article. Interested students can take a look.

Here I would like to remind you, don’t turn corners when looking at the source code, just look at the general logical thinking, guess half and read half, don’t let yourself understand every step of the code, so as to prevent yourself from getting stuck in the source code.

【Summarize】

 To summarize this again, the ViewModel will save a corresponding ViewModelScope, which will be stored in its mBagOfTags Map when it is acquired for the first time, and it will be taken out from this mBagOfTags when it is acquired again. When the ViewModel host body (Activity or Fragment) is destroyed, the Activity or Fragment will clear and release all the data held by the mBagOfTags of the ViewModel through the ViewModelStore. Of course, the coroutine is released and canceled at this time.

Guess you like

Origin blog.csdn.net/Leo_Liang_jie/article/details/120263726