浅谈StrictMode

开篇

在日常开发中,我们或多或少会接触到一些性能问题,比如运行缓慢,ANR等。工欲善其事必先利其器。本文介绍一下StrictMode在Android开发中的作用。

StrictMode的定义

StrictMode(严格模式),一个可以检测  工程师意外产生的错误  的开发工具,并让你通过UI改变让你注意到有错误。StrictMode一般被用来捕获在应用main thread中的意外的硬盘io或网络IO。让硬盘读写和网络操作从main thread中脱离会让程序更流畅,响应更灵敏,同时摆脱了ANR的困扰。
注意:Android设备的硬盘一般都是flash存储器,许多的设备很少并发地将文件系统运行在存储器的顶端。一般来说硬盘IO都是很快的,少部分情况下当IO运行在后台进程中会变得特别的慢。我们应该假定一般都是慢的。这样更合适。


具体能检测什么

严格模式主要检测两大问题,一个是线程策略,即TreadPolicy,另一个是VM策略,即VmPolicy。

ThreadPolicy

线程策略检测的内容有
自定义的耗时调用 使用detectCustomSlowCalls()开启
磁盘读取操作 使用detectDiskReads()开启
磁盘写入操作 使用detectDiskWrites()开启
网络操作 使用detectNetwork()开启

VmPolicy

虚拟机策略检测的内容有
Activity泄露 使用detectActivityLeaks()开启
未关闭的Closable对象泄露 使用detectLeakedClosableObjects()开启
泄露的Sqlite对象 使用detectLeakedSqlLiteObjects()开启
检测实例数量 使用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);
    }

使用范例:

public void onCreate() {
    if (DEVELOPER_MODE) {
        StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                .detectDiskReads()
                .detectDiskWrites()
                .detectNetwork()   // or .detectAll() for all detectable problems
                .penaltyLog()
                .build());
        StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                .detectLeakedSqlLiteObjects()
                .detectLeakedClosableObjects()
                .penaltyLog()
                .penaltyDeath()
                .build());
    }
    super.onCreate();
}

异常捕获、查看结果

严格模式有很多种报告违例的形式,但是想要分析具体违例情况,还是需要查看日志,终端下过滤StrictMode就能得到违例的具体stacktrace信息。

adb logcat | grep StrictMode

日志的时间靠谱么

在下面的过滤日志中,我们看到下面的一个IO操作要消耗31毫秒,这是真的么

D/StrictMode( 2921): StrictMode policy violation; ~duration=31 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=31 violation=2
D/StrictMode( 2921):    at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1176)
D/StrictMode( 2921):    at libcore.io.BlockGuardOs.read(BlockGuardOs.java:148)
D/StrictMode( 2921):    at libcore.io.IoBridge.read(IoBridge.java:422)
D/StrictMode( 2921):    at java.io.FileInputStream.read(FileInputStream.java:179)
D/StrictMode( 2921):    at java.io.InputStreamReader.read(InputStreamReader.java:244)
D/StrictMode( 2921):    at java.io.BufferedReader.fillBuf(BufferedReader.java:130)
D/StrictMode( 2921):    at java.io.BufferedReader.readLine(BufferedReader.java:354)
D/StrictMode( 2921):    at com.example.strictmodedemo.MainActivity.testReadContentOfFile(MainActivity.java:65)
D/StrictMode( 2921):    at com.example.strictmodedemo.MainActivity.onCreate(MainActivity.java:28)
D/StrictMode( 2921):    at android.app.Activity.performCreate(Activity.java:4543)

从上面的stacktrace可以看出testReadContentOfFile方法中包含了文件读取IO操作,至于是否为31毫秒,我们可以利用秒表的原理计算一下,即在方法调用的地方如下记录

long startTime = System.currentTimeMillis();
testReadContentOfFile();
long cost = System.currentTimeMillis() - startTime;
Log.d(LOGTAG, "cost = " + cost);

得到的日志中上述操作耗时9毫秒,非31毫秒。


D/MainActivity(20996): cost = 9

---注:通常情况下StrictMode给出的耗时相对实际情况偏高,并不是真正的耗时数据---


特别需要注意的理念

当你发现一个异常的时候. 比如你通过adb logcat看到了penaltvLog()打出来的log.
你可以用: threads, Handler, AsyncTask, IntentService等尝试修复它. 特别注意别惦记着要修复所有StrictMode找到的点. 在正常活动生命周期中,许多情况下磁盘访问通常是必要的.不过利用StrictMode找到的在UI线程上的网络请求几乎都的确是一个问题。
StrictMode是不是一个安全机制并不能保证发现所有的磁盘或网络访问。
在Bindercalls时跨进程通信的状态下,它还是很有效的一种机制。
从磁盘或网络访问调用JNI不会触发。

1. http://droidyue.com/blog/2015/09/26/android-tuning-tool-strictmode/

2. http://www.bubuko.com/infodetail-1819517.html

3. http://tech.it168.com/a2011/0908/1243/000001243936_all.shtml



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值