美文网首页kotlin
Kotlin ViewModel KTX-内存泄露

Kotlin ViewModel KTX-内存泄露

作者: 未见哥哥 | 来源:发表于2020-06-19 16:06 被阅读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);
            }
        }
    }
    

    参考文档

    ViewModel KTX官方文档

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

    image-20200619160447355

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

    记录于 2020年6月19号

    相关文章

      网友评论

        本文标题:Kotlin ViewModel KTX-内存泄露

        本文链接:https://www.haomeiwen.com/subject/lctbxktx.html