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 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> StrictMode卡顿与泄漏检测-StrictMode原理(2) -> 正文阅读

[移动开发]StrictMode卡顿与泄漏检测-StrictMode原理(2)

2. StrictMode关闭检测原理

StrictMode的关闭检测其实是比较简单的,基本上就是在开启的时候埋桩,释放的时候检测桩子是否有关闭,没有关闭则检测为泄漏。

2.1 CloseGuard

这里有一个预备知识,那就是CloseGuard,它是一个StrictMode专门为了处理此类事情的类,主要包括三个函数:

  • open 开始埋桩
  • close 拆桩
  • warnIfOpen 爆炸
SystemApi(client = MODULE_LIBRARIES)
@libcore.api.IntraCoreApi
public final class CloseGuard {

    private static volatile boolean stackAndTrackingEnabled = true;

    private static volatile Reporter reporter = new DefaultReporter();

    private static volatile Tracker currentTracker = null; // Disabled by default.

    @UnsupportedAppUsage
    @SystemApi(client = MODULE_LIBRARIES)
    public static void setEnabled(boolean enabled) {
        CloseGuard.stackAndTrackingEnabled = enabled;
    }


    public static boolean isEnabled() {
        return stackAndTrackingEnabled;
    }


    @UnsupportedAppUsage(trackingBug=111170242)
    @SystemApi(client = MODULE_LIBRARIES)
    @libcore.api.IntraCoreApi
    public void open(String closer) {
        openWithCallSite(closer, null /* callsite */);
    }

		//关闭之前,
    @UnsupportedAppUsage
    @SystemApi(client = MODULE_LIBRARIES)
    @libcore.api.IntraCoreApi
    public void close() {
        Tracker tracker = currentTracker;
        if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) {
            // Invoke tracker on close only if we invoked it on open. Tracker may have changed.
            tracker.close((Throwable) closerNameOrAllocationInfo);
        }
        closerNameOrAllocationInfo = null;
    }


    public void warnIfOpen() {
        if (closerNameOrAllocationInfo != null) {
            if (closerNameOrAllocationInfo instanceof Throwable) {
                reporter.report(MESSAGE, (Throwable) closerNameOrAllocationInfo);
            } else if (stackAndTrackingEnabled) {
                reporter.report(MESSAGE + " Callsite: " + closerNameOrAllocationInfo);
            } else {
                System.logW("A resource failed to call "
                        + (String) closerNameOrAllocationInfo + ". ");
            }
        }
    }
}

2.1 IO关闭检测

回顾一下之前的例子

        findViewById<Button>(R.id.io_not_close_btn).setOnClickListener {
            var outputStream = FileOutputStream(File(getExternalFilesDir("")?.path + "hello.json"))
            outputStream.write("hello world".toByteArray())
            outputStream = FileOutputStream(File(getExternalFilesDir("")?.path + "hello.json"))
            Runtime.getRuntime().gc()
            Runtime.getRuntime().gc()
        }

埋炸弹 -首先来看看构造函数:

private final CloseGuard guard = CloseGuard.get();
public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
        String name = (file != null ? file.getPath() : null);
	   		.......
        // Android-added: CloseGuard support. Android添加的
        guard.open("close");
  }

拆炸弹 - 关闭输出流

    public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }

        // Android-added: CloseGuard support.
        guard.close();
    }

爆炸 - 没有调用close便被回收

    protected void finalize() throws IOException {
        // Android-added: CloseGuard support.
        if (guard != null) {
            guard.warnIfOpen();
        }
				...
    }

同理推断其他的流也会有其他的机制来检测泄漏。

2.2 IO卡顿检测

看卡顿前,我们需要先准备点预备知识,IO读取与写出的预备知识:

FileInputStream/FileOutputStream -> IOBridge -> ForwardingOs -> BlockGuardOS -> LibCore.OS

我们需要关注的主要在BlockGuardOS这一层,我们先看看源代码:

public class BlockGuardOs extends ForwardingOs {
    @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
        BlockGuard.getThreadPolicy().onReadFromDisk();//检测IO读
        BlockGuard.getVmPolicy().onPathAccess(path);//检测VM策略路径访问
        if ((flags & O_ACCMODE) != O_RDONLY) {
            BlockGuard.getThreadPolicy().onWriteToDisk(); //检测IO写
        }
				....
    }
  
      @UnsupportedAppUsage
    @Override public void close(FileDescriptor fd) throws ErrnoException {
        try {
        
            if (fd.isSocket$()) {
                if (isLingerSocket(fd)) {
                    BlockGuard.getThreadPolicy().onNetwork(); //是否主线程访问网络
                }
            }
        } catch (ErrnoException ignored) {
        }
        super.close(fd);
    }
 
}

这里比较清晰的看到,BlockGuardOs内部代理了BlockGuard,每次调用函数前,都会检测一下是否有卡顿问题。最后再回到StrictMode.

public void onReadFromDisk() {
    if ((mThreadPolicyMask & DETECT_THREAD_DISK_READ) == 0) {
      return;
    }
    if (tooManyViolationsThisLoop()) {
      return;
    }
    //这个函数里面会进行打印
    startHandlingViolationException(new DiskReadViolation());
}

2.3 SqliteCursor泄漏检测

location:frameworks/base/core/java/android/database/sqlite/SQLiteCursor.java

  
protected void finalize() {
        try {
            // if the cursor hasn't been closed yet, close it first
            if (mWindow != null) {
                // Report original sql statement
                if (StrictMode.vmSqliteObjectLeaksEnabled()) {
                    String sql = mQuery.getSql();
                    int len = sql.length();
                    StrictMode.onSqliteObjectLeaked(
                            "Finalizing a Cursor that has not been deactivated or closed. "
                            + "database = " + mQuery.getDatabase().getLabel()
                            + ", table = " + mEditTable
                            + ", query = " + sql.substring(0, (len > 1000) ? 1000 : len),
                            null);
                }
            }
        } finally {
            super.finalize();
        }
    }

很明显就可以看出,当mWindow不为空时,会进行泄漏的消息上报。这里的window指的是cursor的查询窗口,执行过move函数会赋值

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 18:16:18  更:2022-06-25 18:18:07 
 
开发: 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 3:18:42-

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