Android性能调优——严苛模式StrictMode

前情提要

由于在demo中涉及了在主线程的网络操作,当时在开发时仅简单搜索了相关异常的处理方法,于是书写了以下代码:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy
											   .Builder()
											   .permitAll()
											   .build();  
StrictMode.setThreadPolicy(policy); 

这段代码很好的解决了抛异常的问题,但当时并未深究。现在重新审视代码,我认识到这只是将问题隐藏了起来,于是决定研究一下Android中的严苛模式,便有了本文。

什么是严苛模式?

让我们看看Android官方文档中给出的定义:

StrictMode is a developer tool which detects things you might be doing by accident and brings them to your attention so you can fix them.

这段话的意思是说:严苛模式是一个开发人员工具,它检测您可能意外做的事情,并提请您注意,以便您可以修复它们。‎
原来严苛模式就是一个供开发人员使用的“地雷探测器”。那它可以探测出哪些“地雷”呢?让我们再来看看Android官方文档中的解释:

StrictMode is most commonly used to catch accidental disk or network access on the application’s main thread, where UI operations are received and animations take place. Keeping disk and network operations off the main thread makes for much smoother, more responsive applications. By keeping your application’s main thread responsive, you also prevent ANR dialogs from being shown to users.

这段话的意思是:‎"严格模式"最常用于捕获应用程序主线程上的意外磁盘或网络访问,其中接收了 UI 操作并进行了动画。将磁盘和网络操作远离主线,使应用程序更顺畅、响应更灵敏。通过保持应用程序的主要线程响应,您还可以防止‎‎ANR 对话‎‎显示给用户。‎

举个例子来说,如果开发者在UI线程中进行了网络操作或者文件系统的操作,而这些缓慢的操作会严重影响应用的响应能力,甚至出现ANR对话框。而为了在开发中发现这些容易忽略的问题,我们使用严苛模式,在系统检测出主线程违例的情况会做出相应的反应,如日志打印,弹出对话框亦或者崩溃等。换言之,严格模式会将应用的违例细节暴露给开发者方便优化与改善。

严苛模式具体检测的是什么呢?

总的来说,严苛模式主要检测两大问题,一个是线程策略,即TreadPolicy,另一个是VM策略,即VmPolicy

ThreadPolicy线程策略检测:

  • 自定义的耗时调用 使用detectCustomSlowCalls()开启
  • 磁盘读取操作 使用detectDiskReads()开启
  • 磁盘写入操作 使用detectDiskWrites()开启
  • 网络操作 使用detectNetwork()开启
  • 如果你想关闭某一项检测,可以使用对应的permit*方法 permitCustomSlowCalls()、permitDiskReads ()、permitDiskWrites()、permitNetwork()

VmPolicy虚拟机策略检测:

  • Activity泄露 使用detectActivityLeaks()开启
  • 未关闭的Closable对象泄露 使用detectLeakedClosableObjects()开启
  • 泄露的Sqlite对象 使用detectLeakedSqlLiteObjects()开启
  • 检测实例数量 使用setClassInstanceLimit()开启
  • BroadcastReceiver 或者 ServiceConnection 等注册类对象是否被正确释放 使用detectLeakedRegistrationObjects() 检查
  • setClassInstanceLimit(),设置某个类的同时处于内存中的实例上限,可以协助检查内存泄露

工作原理

其实StrictMode实现原理也比较简单,以IO操作为例,主要是通过在open,read,write,close时进行监控。
libcore.io.BlockGuardOs文件就是监控的地方。以open为例,如下进行监控。

@Override
public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
  BlockGuard.getThreadPolicy().onReadFromDisk();
    if ((mode & O_ACCMODE) != O_RDONLY) {
      BlockGuard.getThreadPolicy().onWriteToDisk();
    }
    return os.open(path, flags, mode);
}

其中onReadFromDisk()方法的实现,代码位于StrictMode.java中。

public void onReadFromDisk() {
    if ((mPolicyMask & DETECT_DISK_READ) == 0) {
      return;
    }
    if (tooManyViolationsThisLoop()) {
      return;
    }
    BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask);
    e.fillInStackTrace();
    startHandlingViolationException(e);
}

常见用法

OK,我们现在已经弄懂严苛模式大概是什么了,那应该如何使用呢?

在哪用

严格模式的开启可以放在Application或者Activity以及其他组件的onCreate方法。为了更好地分析应用中的问题,建议放在Application的onCreate方法中。

怎么用

需要注意的是,我们只能在app的开发版本下使用 StrictMode,线上版本应避免使用 StrictMode,可以通过定义一个布尔值变量DEV_MODE来进行控制。

private boolean DEV_MODE = true;
 public void onCreate() {
     if (DEV_MODE) {
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectCustomSlowCalls() //API等级11,使用StrictMode.noteSlowCode
                 .detectDiskReads()
                 .detectDiskWrites()
                 .detectNetwork()   // or .detectAll() for all detectable problems
                 .penaltyLog() //在Logcat 中打印违规异常信息
                 .build());
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectLeakedSqlLiteObjects()
                 .detectLeakedClosableObjects() //API等级11
                 .penaltyLog()
                 .penaltyDeath()
                 .build());
     }
     super.onCreate();
 }

这里的penalty***()方法,是当触发违规条件后会执行的操作,主要有以下几种:

  • penaltyDeath(),当触发违规条件时,直接Crash掉当前应用程序
  • penaltyDeathOnNetwork(),当触发网络违规时,Crash掉当前应用程序
  • penaltyDialog(),触发违规时,显示对违规信息对话框
  • penaltyFlashScreen(),会造成屏幕闪烁,不过一般的设备可能没有这个功能
  • penaltyDropBox(),将违规信息记录到 dropbox系统日志目录中(/data/system/dropbox),你可以通过如下命令进行插件:
查看报告结果

有包括对话框,日志,闪烁屏幕等若干结果显示形式,如果想要获得详细信息,请在日志处查看,在Logcat中搜索StrictMode即可看到相关信息。
在这里插入图片描述

解决违例
  • 如果是主线程中出现文件读写违例,建议使用工作线程(必要时结合Handler)完成。
  • 如果是对SharedPreferences写入操作,建议优先调用apply而非commit。
  • 如果是存在未关闭的Closable对象,根据对应的stacktrace进行关闭。
  • 如果是SQLite对象泄露,根据对应的stacktrace进行释放。

其他技巧

我们可以在手机的开发者选项中打开启用严格模式,这样如果主线程中有执行时间长的操作,屏幕就会闪烁,让我们第一时间感知到。
在这里插入图片描述

注意

  • 在线上环境即Release版本不建议开启严格模式。
  • 严格模式无法监控JNI中的磁盘IO和网络请求。
  • 应用中并非需要解决全部的违例情况,比如有些IO操作必须在主线程中进行。

总结

经过学习,回望自己在开始时写的那段代码,我认识到它只是关闭了对所有线程违例的检测,将错误隐藏了起来,但其实还是存在的,并未得到根本解决,只是程序能够运行了而已。不过借此机会了解了这个知识点,今后也应注意,不能在开发过程中具有“拿来主义”的思想,对这些自己不了解的东西不求甚解。

参考文档:
Android严苛模式StrictMode使用详解
Android性能调优利器StrictMode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值