一文瞭解Android ViewModelScope 如何自動取消協程
先看一下 ViewModel 中的 ViewModelScope 是何方神聖
val ViewModel.viewModelScope: CoroutineScope get() { val scope: CoroutineScope? = this.getTag(JOB_KEY) if (scope != null) { return scope } return setTagIfAbsent(JOB_KEY, CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)) }
可以看到這個是一個擴展方法,
再點擊 setTagIfAbsent 方法進去
<T> T setTagIfAbsent(String key, T newValue) { T previous; synchronized (mBagOfTags) { previous = (T) mBagOfTags.get(key);//第一次肯定為null if (previous == null) { mBagOfTags.put(key, newValue);//null 存儲 } } T result = previous == null ? newValue : previous; if (mCleared) {//判斷是否已經clear瞭 // It is possible that we'll call close() multiple times on the same object, but // Closeable interface requires close method to be idempotent: // "if the stream is already closed then invoking this method has no effect." (c) closeWithRuntimeException(result); } return result; }
可以看到 這邊 會把 我們的 ViewModel 存儲到 ViewModel 內的 mBagOfTags 中
這個 mBagOfTags 是
private final Map<String, Object> mBagOfTags = new HashMap<>();
這個時候 我們 viewModel 就會持有 我們 viewModelScope 的協程 作用域瞭。那..這也隻是 表述瞭 我們 viewModelScope 存在哪裡而已,什麼時候清除呢?
先看一下 ViewModel 的生命周期:
可以看到 ViewModel 的生命周期 會在 Activity onDestory 之後會被調用。那…具體哪裡調的?
翻看源碼可以追溯到 ComponentActivity 的默認構造器內
public ComponentActivity() { /*省略一些*/ getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } }); }
可以看到內部會通對 Lifecycle 添加一個觀察者,觀察當前 Activity 的生命周期變更事件,如果走到瞭 Destory ,並且 本次 Destory 並非由於配置變更引起的,才會真正調用 ViewModelStore 的 clear 方法。
跟進 clear 方法看看:
public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
可以看到這個 ViewModelStore 內部實現 用 HashMap 存儲 ViewModel
於是在 clear 的時候,會逐個遍歷調用 clear方法,再次跟進 ViewModel 的 clear 方法
@MainThread final void clear() { mCleared = true; // Since clear() is final, this method is still called on mock objects // and in those cases, mBagOfTags is null. It'll always be empty though // because setTagIfAbsent and getTag are not final so we can skip // clearing it if (mBagOfTags != null) { synchronized (mBagOfTags) { for (Object value : mBagOfTags.values()) { // see comment for the similar call in setTagIfAbsent closeWithRuntimeException(value); } } } onCleared(); }
可以發現我們最初 存放 viewmodelScope 的 mBagOfTags
這裡面的邏輯 就是對 mBagOfTags 存儲的數據 挨個提取出來並且調用 closeWithRuntimeException
跟進 closeWithRuntimeException:
private static void closeWithRuntimeException(Object obj) { if (obj instanceof Closeable) { try { ((Closeable) obj).close(); } catch (IOException e) { throw new RuntimeException(e); } } }
該方法內會逐個判斷 對象是否實現 Closeable 如果實現就會調用這個接口的 close 方法,
再回到最初 我們 viewModel 的擴展方法那邊,看看我們 viewModelScope 的真正面目
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope { override val coroutineContext: CoroutineContext = context override fun close() { coroutineContext.cancel() } }
可以明確的看到 我們的 ViewModelScope 實現瞭 Closeable 並且充寫瞭 close 方法,
close 方法內的實現 會對 協程上下文進行 cancel。
至此我們 可以大致整理一下:
- viewModelScope 是 ViewModel 的擴展成員,該對象是 CloseableCoroutineScope,並且實現瞭 Closeable 接口
- ViewModelScope 存儲在 ViewModel 的 名叫 mBagOfTags 的HashMap中 啊
- ViewModel 存儲在 Activity 的 ViewModelStore 中,並且會監聽 Activity 的 Lifecycle 的狀態變更,在ON_DESTROY 且 非配置變更引起的事件中 對 viewModelStore 進行清空
- ViewModelStore 清空會對 ViewModelStore 內的所有 ViewModel 逐個調用 clear 方法。
- ViewModel的clear方法會對 ViewModel的 mBagOfTags 內存儲的對象進行調用 close 方法(該對象需實現Closeable 接口)
- 最終會會調用 我們 ViewModelScope 的實現類 CloseableCoroutineScope 的 close 方法中。close 方法會對協程進行 cancel。
到此這篇關於一文瞭解Android ViewModelScope 如何自動取消協程的文章就介紹到這瞭,更多相關Android ViewModel Scope 取消協程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 源碼解析Android Jetpack組件之ViewModel的使用
- Android Jetpack庫剖析之ViewModel組件篇
- Kotlin創建一個好用的協程作用域
- Android開發Jetpack組件Lifecycle使用篇
- Android Jetpack 組件LiveData源碼解析