上一篇文章 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 持有資料全部清除釋放掉,當然協程就是在這個時候放取消掉,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/300017.html
標籤:其他
