Kotlin ViewModel KTX-内存泄露
2020-06-19 本文已影响0人
未见哥哥
协程内存泄露-ViewModel
ViewModel KTX 中提供了 viewModelScope
,目的是为了减少协程内存泄露。
如何使用
将 GlobalScope
替换为 viewModelScope
即可。
viewModelScope.launch(Dispatchers.Main) {
showLoadingLiveData.postValue(true)
//将文件转化为ByteString
Logz.d("获取ByteString")
val uploadBuffer = getUploadBuffer(uploadTempFile)
//......
uploadFile(uploadBuffer)
}
工作原理
常规情况使用协程需要手动去停止对应的协程,如果没有正确的调用则会出现内存泄露问题,而 ViewModel KTX 提供的viewModelScope
则自动帮我们做了这件事。
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))
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
如何取消协程
从源码可以看到,内部提供了一个 CloseableCoroutineScope
,并且调用它的 close 方法即可将协程cancel。
那么关键我们需要关注什么时候调用这个 close 方法。
如何做到主动取消?
我们知道 ViewModel 当被清除时会回调 onClear() 方法,我们从这个方法中去找对应取消协程相关的操作。
下面是 ViewModel 的两个方法的源码。onClear()是在 clear() 中调用的,并且会调用 closeWithRuntimeException()
,在这里可以看到它会检测当前是 Closeable 类型的对应则会主动调用 close(),回到上面提到的 CloseableCoroutineScope
这个类就实现了 Closeable
接口,因此我们就找到了主动取消协程的地方了。
//ViewModel.kt
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@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();
}
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
参考文档
image-20200619160447355不用担心引入库导致包体积增大,这些扩展库的体积都很小
本文是笔者学习之后的总结,方便日后查看学习,有任何不对的地方请指正。
记录于 2020年6月19号