Android性能调优利器StrictMode
mayday
8年前
<p>作为Android开发,日常的开发工作中或多或少要接触到性能问题,比如我的Android程序运行缓慢卡顿,并且常常出现ANR对话框等等问题。既然有性能问题,就需要进行性能优化。正所谓工欲善其事,必先利其器。一个好的工具,可以帮助我们发现并定位问题,进而有的放矢进行解决。本文主要介绍StrictMode 在Android 应用开发中的应用和一些问题。</p> <h2>什么是StrictMode</h2> <p>StrictMode意思为严格模式,是用来检测程序中违例情况的开发者工具。最常用的场景就是检测主线程中本地磁盘和网络读写等耗时的操作。</p> <h3>严在哪里</h3> <p>既然叫做严格模式,那么又严格在哪些地方呢?</p> <p>在Android中,主线程,也就是UI线程,除了负责处理UI相关的操作外,还可以执行文件读取或者数据库读写操作(从Android 4.0 开始,网络操作禁止在主线程中执行,否则会抛出 <a href="/misc/goto?guid=4959644472615567262" rel="nofollow,noindex">NetworkOnMainThreadException</a> )。使用严格模式,系统检测出主线程违例的情况会做出相应的反应,如日志打印,弹出对话框亦或者崩溃等。换言之,严格模式会将应用的违例细节暴露给开发者方便优化与改善。</p> <h3>具体能检测什么</h3> <p>严格模式主要检测两大问题,一个是线程策略,即TreadPolicy,另一个是VM策略,即VmPolicy。</p> <h3>ThreadPolicy</h3> <p>线程策略检测的内容有</p> <ul> <li>自定义的耗时调用 使用 <strong>detectCustomSlowCalls()</strong> 开启</li> <li>磁盘读取操作 使用 <strong>detectDiskReads()</strong> 开启</li> <li>磁盘写入操作 使用 <strong>detectDiskWrites()</strong> 开启</li> <li>网络操作 使用 <strong>detectNetwork()</strong> 开启</li> </ul> <h3>VmPolicy</h3> <p>虚拟机策略检测的内容有</p> <ul> <li>Activity泄露 使用 <strong>detectActivityLeaks()</strong> 开启</li> <li>未关闭的Closable对象泄露 使用 <strong>detectLeakedClosableObjects()</strong> 开启</li> <li>泄露的Sqlite对象 使用 <strong>detectLeakedSqlLiteObjects()</strong> 开启</li> <li>检测实例数量 使用 <strong>setClassInstanceLimit()</strong> 开启</li> </ul> <h2>工作原理</h2> <p>其实StrictMode实现原理也比较简单,以IO操作为例,主要是通过在open,read,write,close时进行监控。 libcore.io.BlockGuardOs 文件就是监控的地方。以open为例,如下进行监控</p> <pre> <code class="language-java">@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); }</code></pre> <p>其中 <strong>onReadFromDisk()</strong> 方法的实现,代码位于StrictMode.java中。</p> <pre> <code class="language-java">public void onReadFromDisk() { if ((mPolicyMask & DETECT_DISK_READ) == 0) { return; } if (tooManyViolationsThisLoop()) { return; } BlockGuard.BlockGuardPolicyException e = new StrictModeDiskReadViolation(mPolicyMask); e.fillInStackTrace(); startHandlingViolationException(e); }</code></pre> <h2>如何使用</h2> <p>关于StrictMode如何使用,最重要的就是如何启用严格模式。</p> <h3>放在哪里</h3> <p>严格模式的开启可以放在Application或者Activity以及其他组件的onCreate方法。为了更好地分析应用中的问题,建议放在Application的onCreate方法中。</p> <h3>简单启用</h3> <p>以下的代码启用全部的ThreadPolicy和VmPolicy违例检测</p> <pre> <code class="language-java">if (IS_DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build()); StrictMode.setVmPolicy(new VmPolicy.Builder().detectAll().penaltyLog().build()); }</code></pre> <p>严格模式需要在debug模式开启,不要在release版本中启用。</p> <p>同时,严格模式自API 9 开始引入,某些API方法也从 API 11 引入。使用时应该注意 API 级别。</p> <p>如有需要,也可以开启部分的严格模式。</p> <h3>查看结果</h3> <p>严格模式有很多种报告违例的形式,但是想要分析具体违例情况,还是需要查看日志,终端下过滤StrictMode就能得到违例的具体stacktrace信息。</p> <pre> <code class="language-java">adb logcat | grep StrictMode</code></pre> <h2>解决违例</h2> <ul> <li>如果是主线程中出现文件读写违例,建议使用工作线程(必要时结合Handler)完成。</li> <li>如果是对SharedPreferences写入操作,在API 9 以上 建议优先调用apply而非commit。</li> <li>如果是存在未关闭的Closable对象,根据对应的stacktrace进行关闭。</li> <li>如果是SQLite对象泄露,根据对应的stacktrace进行释放。</li> </ul> <h2>其他技巧</h2> <p>除了通过日志查看之外,我们也可以在开发者选项中开启严格模式,开启之后,如果主线程中有执行时间长的操作,屏幕则会闪烁,这是一个更加直接的方法。</p> <h2>问题来了</h2> <p>通常情况下StrictMode给出的耗时相对实际情况偏高,并不是真正的耗时数据。</p> <ul> <li>在线上环境即Release版本不建议开启严格模式。</li> <li>严格模式无法监控JNI中的磁盘IO和网络请求。</li> <li>应用中并非需要解决全部的违例情况,比如有些IO操作必须在主线程中进行。</li> </ul> <p> </p> <p>来自:http://www.jianshu.com/p/eb1f0b5301ee</p> <p> </p>