ViewModelScope 簡介與使用

簡介

  • 當在ViewModel中引入協程,如果直接使用CoroutineScope,那麼需要在onCleared()方法中取消協程,如果忘記取消協程那麼會導致出現內存泄漏等各種問題,此時需要使用ViewModel擴展屬性viewModelScope來實現協程作用域。

viewModelScope源碼分析

val ViewModel.viewModelScope: CoroutineScope
        get() {
            val scope: CoroutineScope? = this.getTag(JOB_KEY)
            if (scope != null) {
                return scope
            }
            // 默認使用Dispatchers.Main,方便Activity和Fragment更新UI
            return setTagIfAbsent(JOB_KEY,
                CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
        }

// 爲了ViewModel能夠取消協程,需要實現Closeable接口
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context


    override fun close() {
        coroutineContext.cancel()
    }
}

ViewModel如何創建以及取消CorutineScope

abstract class ViewModel
    // ViewModel通過HashMap存儲CoroutineScope對象
    private final Map<String, Object> mBagOfTags = new HashMap<>();

    // ViewModel被銷燬時內部執行clear()方法,
    @MainThread
    final void clear() {
        mCleared = true;
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // 取消viewModelScope作用域的協程
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }

// 取消CoroutineScope
    private static void closeWithRuntimeException(Object obj) {
        if (obj instanceof Closeable) {
            try {
                ((Closeable) obj).close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }


    // 如果JOB_KEY已存在且對應的協程作用域不爲空則返回對象,否則創建新的協程作用域,並設置JOB_KEY
    @SuppressWarnings("unchecked")
    <T> T setTagIfAbsent(String key, T newValue) {
        T previous;
        synchronized (mBagOfTags) {
            previous = (T) mBagOfTags.get(key);
            if (previous == null) {
                mBagOfTags.put(key, newValue);
            }
        }
        T result = previous == null ? newValue : previous;
        if (mCleared) {
            closeWithRuntimeException(result);
        }
        return result;
    }

    // 通過JOB_KEY從HashMap中返回相應的協程作用域
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    <T> T getTag(String key) {
        synchronized (mBagOfTags) {
            return (T) mBagOfTags.get(key);
        }
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章