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函数会赋值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值