Kotlin ViewModel KTX-内存泄露

协程内存泄露-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);
        }
    }
}

参考文档

ViewModel KTX官方文档

不用担心引入库导致包体积增大,这些扩展库的体积都很小

image-20200619160447355

本文是笔者学习之后的总结,方便日后查看学习,有任何不对的地方请指正。

记录于 2020年6月19号

你可能感兴趣的:(Kotlin ViewModel KTX-内存泄露)