浅谈StrictMode

来源:互联网 发布:花生壳免费域名使用 编辑:程序博客网 时间:2024/05/28 11:30

开篇

在日常开发中,我们或多或少会接触到一些性能问题,比如运行缓慢,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=2D/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



0 0
原创粉丝点击