IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Room RoomTrackingLiveData导致的内存泄漏 -> 正文阅读

[移动开发]Room RoomTrackingLiveData导致的内存泄漏

在使用Room RoomTrackingLiveData的时候,发现一个内存泄漏,为了解决RoomTrackingLiveData在一直写数据库的时候,不回调的问题,我自己重写了一个SHRoomTrackingLiveData,正因为重新后再里面加log发现了内存泄漏,Activity/Fragment已经退出了,SHRoomTrackingLiveData还活着,还在一直查数据库,导致一系列的对象泄漏,而且一直运行代码手机发热。
内存泄漏,我们得发现引用关系,才能知道怎么泄漏的,可以用Android profiler抓一下,看下面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
泄漏原因是一个Runable mRefreshRunable在子线程中执行,它引用了外部类对象SHRoomTrackingLiveData,图中编号1,SHRoomTrackingLiveData中通过mObserver引用了InvalidationTracker.Observer对象,编号2,同时InvalidationTracker.Observer对象被弱引用着。

在这种状态下,我们退出activity,子线程还在执行,上图的引用关系就还在,由于编号2引用的存储,导致编号3引用释放不掉,现在又不断的更新数据库InvalidationTracker.Observer重置mInvalid变量,又导致子线程结束不了,陷入到了循环里,导致内存泄漏。

要解决这个内存泄漏,就得让子线程退出,子线程退出了,SHRoomTrackingLiveData被回收了,编号2引用断了,编号3弱引用自然也就断了。这个是google设计者就这么设计的。
理论上,不密集的写数据库,子线程执行完会自动退出的。
在这里插入图片描述
这个就是编号3弱引用的地方

但是,我们的业务场景是密集的写数据库,所以导致线程退出不了。为了解决这个问题,我们在退出activity的时候,把编号3引用干掉,那么线程最终会退出的。
在这里插入图片描述
在这里插入图片描述
这是改写的地方,可以对照着RoomTrackingLiveData源码看就理解了。
这里说一点,本来想在onInactive中,把编号3引用干掉,结果,导致了新问题。跳转到一个新activity再回来,onInactive是执行的。看注释。其实我们只是想退出activity的时候才干掉引用。

SHRoomTrackingLiveData完整代码

package androidx.room;

import android.annotation.SuppressLint;

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.lifecycle.LiveData;
import androidx.room.InvalidationTracker;
import androidx.room.RoomDatabase;

import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Author zhongyili
 * @Date 2022/6/21
 *
 * 模仿RoomTrackingLiveData,Callable执行完都通知LiveData更新数据
 * RoomTrackingLiveData原有逻辑是:Callable执行完,如果是有效的(mInvalid false)才通知LiveData更新数据
 */
public class SHRoomTrackingLiveData<T> extends LiveData<T> {
    @SuppressWarnings("WeakerAccess")
    final RoomDatabase mDatabase;

    @SuppressWarnings("WeakerAccess")
    final Callable<T> mComputeFunction;

    @SuppressWarnings("WeakerAccess")
    final InvalidationTracker.Observer mObserver;

    @SuppressWarnings("WeakerAccess")
    final AtomicBoolean mInvalid = new AtomicBoolean(true);

    @SuppressWarnings("WeakerAccess")
    final AtomicBoolean mComputing = new AtomicBoolean(false);

    @SuppressWarnings("WeakerAccess")
    final AtomicBoolean mRegisteredObserver = new AtomicBoolean(false);

    @SuppressWarnings("WeakerAccess")
    final Runnable mRefreshRunnable = new Runnable() {
        @SuppressLint("RestrictedApi")
        @WorkerThread
        @Override
        public void run() {
            if (mRegisteredObserver.compareAndSet(false, true)) {
                mDatabase.getInvalidationTracker().addObserver(mObserver);
            }
            boolean computed;
            do {
                computed = false;
                // compute can happen only in 1 thread but no reason to lock others.
                if (mComputing.compareAndSet(false, true)) {
                    // as long as it is invalid, keep computing.
                    try {
                        T value = null;
                        while (mInvalid.compareAndSet(true, false)) {
                            computed = true;
                            try {
                                value = mComputeFunction.call();
                            } catch (Exception e) {
                                throw new RuntimeException("Exception while computing database"
                                        + " live data.", e);
                            }
                            if (computed) {
                                postValue(value);
                            }
                        }
                    } finally {
                        // release compute lock
                        mComputing.set(false);
                    }
                }
                // check invalid after releasing compute lock to avoid the following scenario.
                // Thread A runs compute()
                // Thread A checks invalid, it is false
                // Main thread sets invalid to true
                // Thread B runs, fails to acquire compute lock and skips
                // Thread A releases compute lock
                // We've left invalid in set state. The check below recovers.
            } while (computed && mInvalid.get());
        }
    };

    @SuppressWarnings("WeakerAccess")
    final Runnable mInvalidationRunnable = new Runnable() {
        @MainThread
        @Override
        public void run() {
            boolean isActive = hasActiveObservers();
            if (mInvalid.compareAndSet(false, true)) {
                if (isActive) {
                    getQueryExecutor().execute(mRefreshRunnable);
                }
            }
        }
    };
    @SuppressLint("RestrictedApi")
    public SHRoomTrackingLiveData(
            RoomDatabase database,
            Callable<T> computeFunction,
            String[] tableNames) {
        mDatabase = database;
        mComputeFunction = computeFunction;
        mObserver = new InvalidationTracker.Observer(tableNames) {
            @Override
            public void onInvalidated(@NonNull Set<String> tables) {
                ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
            }
        };
    }

    @Override
    protected void onActive() {
        super.onActive();
        getQueryExecutor().execute(mRefreshRunnable);
    }

    /**
     * !!!!!!!!
     * 这个表示是Inactive 不活跃,并不能代表Activity/Fragment destroy
     * stop的时候,也是inactive
     */
    @Override
    protected void onInactive() {
        super.onInactive();
    }

    public void onDestroy() {
        mDatabase.getInvalidationTracker().removeObserver(mObserver);
        mInvalid.set(false);
    }

    Executor getQueryExecutor() {
        return mDatabase.getQueryExecutor();
    }
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-17 16:34:46  更:2022-07-17 16:37:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 2:30:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码