StrictMode:严格模式的使用
第一次接触使用StrictMode还是刚毕业的时候,现在再次记录一下StrictMode。本篇主要详细了解StrictMode的使用方法。关于StrictMode的实现原理详见另外一篇StrictMode原理剖析。
1、StrictMode
StrictMode是从Android 2.3(API 9)开始引入的调试工具。通常帮助开发者用来检测应用中出现的主线程I/O、主线程进行网络访问、内存泄漏等违规问题。
切记,StrictMode必须只在debug版本启用,否则会对用户正常使用造成干扰。通常用BuildComfig.DEBUG条件判断。
if (BuildConfig.DEBUG) {
StrictMode.VmPolicy vmPolicy = new StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.detectCleartextNetwork()
.detectFileUriExposure()
.detectLeakedClosableObjects()
.detectContentUriWithoutPermission()
.build();
StrictMode.setVmPolicy(vmPolicy);
StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder()
.permitDiskReads()
.detectDiskWrites()
.detectNetwork()
.detectCustomSlowCalls()
.penaltyDialog()
.build();
StrictMode.setThreadPolicy(threadPolicy);
}
2、StrictMode策略
StrictMode中有两种策略:一个是线程策略,即ThreadPolicy,针对具体的线程;另一个是虚拟机策略,即VmPolicy,针对虚拟机中的所有对象。
分别通过以下两个方法设置:
//设置StrictMode虚拟机相关的策略
StrictMode.setVmPolicy(vmPolicy)
//设置StrictMode线程相关的策略
StrictMode.setThreadPolicy(threadPolicy)
VmPolicy和ThreadPolicy这两种策略都是StrictMode内部类,无法直接通过构造函数创建,两种策略内部都分别提供了Builder构造者创建各自示例。使用建造者模式的好处是开发者无需记住每种检测的flag,只需要通过构造者提供的方法去“定制”自己想要的策略。
除了构造策略,还可以从StrictMode中拿出已有的策略修改使用:
StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy();
StrictMode.ThreadPolicy newer = new StrictMode.ThreadPolicy.Builder(old)
.permitDiskWrites()
.build();
StrictMode.setThreadPolicy(newer);
下面对StrictMode中的两种策略进行详细的介绍。
2.1、VmPolicy
VmPolicy是虚拟机相关的策略。用来检测SQLiteCursor没关闭、实现Closable接口的类使用后没关闭等。通过内置的VmPolicy.Builder类构造VmPolicy实例:
val vmPolicyBuilder: StrictMode.VmPolicy.Builder = StrictMode.VmPolicy.Builder()
VmPolicy可以设置检测(detect)如下若干项:
vmPolicyBuilder.apply {
setClassInstanceLimit(Class klass, int instanceLimit)
detectActivityLeaks()
detectNonSdkApiUsage()
detectLeakedSqlLiteObjects()
detectLeakedClosableObjects()
detectLeakedRegistrationObjects()
detectFileUriExposure()
detectCleartextNetwork()
detectContentUriWithoutPermission()
detectUntaggedSockets()
detectImplicitDirectBoot()
detectCredentialProtectedWhileLocked()
//detectIncorrectContextUse()
detectAll()
...
}
也可以设置允许(permit)某些违规:
vmPolicyBuilder.apply {
...
//permitActivityLeaks()
permitNonSdkApiUsage()
//permitUntaggedSockets()
//permitImplicitDirectBoot()
//permitCredentialProtectedWhileLocked()
//permitIncorrectContextUse()
...
}
当发生违反VM策略的情况发生时,进行一些“惩罚”,有如下若干“penalty”项可选:输出log、弹框、“自杀”等等。
vmPolicyBuilder.apply {
...
penaltyDeath()
penaltyDeathOnCleartextNetwork()
penaltyDeathOnFileUriExposure()
penaltyLog()
penaltyDropBox()
penaltyListener(Executor executor,OnVmViolationListener listener)
penaltyListener(OnVmViolationListener listener,Executor executor)
}
最后,将构造好的VmPolicy实例设置给StrictMode:
val vmPolicy: StrictMode.VmPolicy = vmPolicyBuilder.build()
StrictMode.setVmPolicy(vmPolicy)
2.2、ThreadPolicy
ThreadPolicy是线程相关策略。可以检测出主线程访问网络、磁盘I/O、慢代码等违规操作。通过内置的ThreadPolicy.Builder类构造ThreadPolicy实例:
val threadPolicyBuilder: StrictMode.ThreadPolicy.Builder = StrictMode.ThreadPolicy.Builder()
设置下面的检测项,能够帮助开发者检测(detect)这些操作:
threadPolicyBuilder.apply {
detectNetwork()
detectDiskReads()
detectDiskWrites()
detectCustomSlowCalls()
detectUnbufferedIo()
detectResourceMismatches()
//detectExplicitGc()
detectAll()
...
}
还可以允许(permit)进行哪些操作:
threadPolicyBuilder.apply {
...
permitNetwork()
permitDiskReads()
permitDiskWrites()
permitCustomSlowCalls()
permitResourceMismatches()
permitUnbufferedIo()
//permitExplicitGc()
permitAll()
...
}
一旦出现了违规(violation),可以设置惩罚(penalty),进行相应的提示,比如 Log、弹框等。
threadPolicyBuilder.apply {
...
//
penaltyDialog()
penaltyDeath()
penaltyDeathOnNetwork()
penaltyFlashScreen()
penaltyLog()
penaltyDropBox()
penaltyListener(Executor executor,OnThreadViolationListener listener)
penaltyListener(OnThreadViolationListener listener,Executor executor)
}
最后,将构造好的ThreadPolicy实例设置给StrictMode:
val threadPolicy = threadPolicyBuilder.build()
StrictMode.setThreadPolicy(threadPolicy)
3、几种Penalty
如何通过StrictMode检测出来?就是通过设置的penalty告知开发者发生违例操作,往往是借助Log、dropbox等定位问题。
3.1、弹框提醒
设置了penaltyDialog(),当发生违反策略的情况发生时会弹框提醒开发者。不建议使用,很烦,因为可能会一直弹框,而且没有什么有用的信息。
3.2、打印Log
设置了penaltyLog(),当发生违反策略的情况发生时输出D/StrictMode日志,包括详细的方法trace,可以定位到违例操作的代码处。
3.3、输出dropbox
除了打印Log,当然可以将信息输出到dropbox文件中,保存关键信息。dropbox文件目录路径一般是/data/system/dropbox。
StrictMode使用就介绍到这里,用法很简单,因为框架在内部做了大量的“插桩”以便监控各种操作,接下来深入了解StrictMode的实现原理,详见StrictMode原理剖析。