前言
StrictMode(严格模式)是一个开发工具,它可以检测出一些影响应用性能的问题。比如,在主线程中进行磁盘读写操作、Activity内存泄漏等。我们可以使用StrictMode来优化应用的性能,提高应用的响应能力,减少应用的内存泄露,避免应用出现ANR和OOM。
使用StrictMode
StrictMode支持两类检测策略:
- ThreadPolicy(线程策略),主要可以检测主线程中的一些耗时操作。
- VmPolicy(虚拟机策略),主要可以检测一些对象的泄漏。
配置ThreadPolicy
调用如下的静态方法可以开启ThreadPolicy检测:
StrictMode.setThreadPolicy(final ThreadPolicy policy);
ThreadPolicy对象的创建需要使用 StrictMode.ThreadPolicy.Builder 类,Builder类提供了一系列的方法来配置ThreadPolicy的检测内容。如下表所示:
方法 | 说明 |
---|---|
detectDiskReads() | 开启磁盘读操作检测 |
detectDiskWrites() | 开启磁盘写操作检测 |
detectNetwork() | 开启网络操作检测 |
detectCustomSlowCalls() | 开启耗时调用检测 |
detectResourceMismatches() | 开启资源类型和获取调用不匹配检测 |
detectAll() | 开启ThreadPolicy支持的所有检测 |
同时,Builder类提供了方法来配置当检测到ThreadPolicy类型的违规时该如何处理。如下表所示:
方法 | 说明 |
---|---|
penaltyLog() | 记录Log |
penaltyDropBox() | 记录Log到DropBox |
penaltyFlashScreen() | 闪烁屏幕 |
penaltyDialog() | 弹出对话框 |
penaltyDeath() | 杀死应用进程 |
penaltyDeathOnNetwork() | 当检测到网络操作时杀死应用进程 |
配置ThreadPolicy的示例代码如下所示:
StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.penaltyFlashScreen()
.penaltyLog()
.build();
StrictMode.setThreadPolicy(threadPolicy);
配置VmPolicy
调用如下的静态方法可以开启VmPolicy检测:
StrictMode.setVmPolicy(final VmPolicy policy);
与ThreadPolicy类似,VmPolicy对象的创建需要使用 StrictMode.VmPolicy.Builder 类,Builder类提供了一系列的方法来配置VmPolicy的检测内容。如下表所示:
方法 | 说明 |
---|---|
detectActivityLeaks() | 开启Activity泄漏检测 |
detectLeakedClosableObjects() | 开启Closeable对象未关闭泄漏检测 |
detectLeakedSqlLiteObjects() | 开启SQLite对象未关闭泄漏检测 |
detectLeakedRegistrationObjects() | 开启BroadcastReceiver/ServiceConnection泄漏检测 |
detectFileUriExposure() | 开启文件URI暴露检测 |
detectCleartextNetwork() | 开启明文网络流量检测 |
detectAll() | 开启VmPolicy支持的所有检测 |
同时,Builder类提供了方法来配置当检测到VmPolicy类型的违规时该如何处理。如下表所示:
方法 | 说明 |
---|---|
penaltyLog() | 记录Log |
penaltyDropBox() | 记录Log到DropBox |
penaltyDeath() | 杀死应用进程 |
penaltyDeathOnFileUriExposure() | 当检测到文件URI暴露时杀死应用进程 |
penaltyDeathOnCleartextNetwork() | 当检测到明文网络流量时杀死应用进程 |
配置VmPolicy的示例代码如下所示:
StrictMode.VmPolicy vmPolicy = new StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectLeakedClosableObjects()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.build();
StrictMode.setVmPolicy(vmPolicy);
开启StrictMode
通常,在应用的Application类、Activity类,或者其它应用组件的onCreate()方法的开始处开启StrictMode。示例代码如下所示:
@Override
public void onCreate() {
// Debug模式下开启严格模式
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyFlashScreen()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
}
super.onCreate();
...
}
注意: 应该在Debug模式下才开启StrictMode。
我们可以使用 StrictMode 关键字来过滤系统日志,根据StrictMode日志我们就可以优化应用了。当检测到在主线程中执行了磁盘读操作时,打印的Log如下所示:
12-07 22:03:30.272 11351-11351/com.github.cyc.performance.strictmodedemo D/StrictMode: StrictMode policy violation; ~duration=2 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=1114143 violation=2
at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:1263)
at libcore.io.BlockGuardOs.read(BlockGuardOs.java:229)
at libcore.io.IoBridge.read(IoBridge.java:468)
at java.io.FileInputStream.read(FileInputStream.java:177)
at java.io.InputStreamReader.read(InputStreamReader.java:233)
at java.io.BufferedReader.fillBuf(BufferedReader.java:145)
at java.io.BufferedReader.readLine(BufferedReader.java:397)
at com.github.cyc.performance.strictmodedemo.FileUtils.readFile(FileUtils.java:22)
at com.github.cyc.performance.strictmodedemo.MainActivity.onClick(MainActivity.java:48)
at android.view.View.performClick(View.java:5207)
at android.view.View$PerformClick.run(View.java:21177)
at android.os.Handler.handleCallback(Handler.java:742)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:5524)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:740)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:630)
当检测到在主线程中执行了磁盘写操作时,打印的Log如下所示:
12-07 22:07:43.083 11351-11351/com.github.cyc.performance.strictmodedemo D/StrictMode: StrictMode policy violation; ~duration=1 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=1114143 violation=1
at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1223)
at libcore.io.BlockGuardOs.write(BlockGuardOs.java:312)
at libcore.io.IoBridge.write(IoBridge.java:493)
at java.io.FileOutputStream.write(FileOutputStream.java:186)
at java.io.OutputStreamWriter.flushBytes(OutputStreamWriter.java:170)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:161)
at java.io.BufferedWriter.flush(BufferedWriter.java:124)
at com.github.cyc.performance.strictmodedemo.FileUtils.writeFile(FileUtils.java:52)
at com.github.cyc.performance.strictmodedemo.MainActivity.onClick(MainActivity.java:53)
at android.view.View.performClick(View.java:5207)
at android.view.View$PerformClick.run(View.java:21177)
at android.os.Handler.handleCallback(Handler.java:742)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:5524)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:740)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:630)
最后要注意:
- 在正常的Activity生命周期中,有些磁盘操作是必要的。
- StrictMode无法检测来自JNI调用的磁盘和网络操作。
总结
我们可以使用StrictMode来优化应用的性能,提高应用的响应能力,减少应用的内存泄露,避免应用出现ANR和OOM。
参考
- Android 7.1.1 (API level 25)
- https://developer.android.com/reference/android/os/StrictMode.html
- https://developer.android.com/reference/android/os/StrictMode.ThreadPolicy.Builder.html
- https://developer.android.com/reference/android/os/StrictMode.VmPolicy.Builder.html