简介

  • 当在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);
}
}

}