2. 线程策略与虚拟机策略:两大检测维度的职责划分
StrictMode 的检测能力被拆为两个正交策略:ThreadPolicy(线程策略) 和 VmPolicy(虚拟机策略)。前者监控主线程上的违规操作(磁盘 I/O、网络、自定义耗时等);后者监控整个虚拟机生命周期中的资源泄漏(未关闭的 Cursor、未关闭的流、Activity 泄漏、注册的 BroadcastReceiver 未注销等)。理解两者的职责边界,是精准配置 StrictMode 的前提。
2.1 ThreadPolicy:主线程“红线”监控
ThreadPolicy 关注的是“当前线程”(默认为 UI 线程)上不允许出现的耗时代码。一旦在主线程上触发磁盘读写、网络请求或自定义慢调用,StrictMode 会通过 Log 输出、弹警告对话框或直接抛异常来提示开发者。
| 检测维度 | 检测开关 (flag) | 默认行为 | 典型场景 |
|---|---|---|---|
| 磁盘读取 | detectDiskReads() | 写 Log + VMDebug 栈 | SharedPreferences 读取、FileInputStream |
| 磁盘写入 | detectDiskWrites() | 同上 | 数据库写操作、文件输出流 |
| 网络操作 | detectNetwork() | 同上 | HttpURLConnection、OkHttp 同步请求 |
| 自定义耗时 | detectCustomSlowCalls() | Log 警告 | 自定义 Runnable/方法标识 |
| 所有违规 | detectAll() | 按声明组合 | 全局快速启用 |
配置范例: 在 Application#onCreate() 中启用线程策略,并将违规行为判定为异常以便快速修复。
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog() // 输出 Logcat 警告
.penaltyDeath() // 直接崩溃(仅限 DEBUG)
.build());
}
2.2 VmPolicy:全局资源泄漏“扫描仪”
VmPolicy 不绑定具体线程,而是监控整个 Dalvik/ART 虚拟机的资源分配与释放是否匹配。它通过 GC 后的残留对象来判断是否发生了泄漏。
| 检测维度 | 检测开关 (flag) | 生效时机 | 常见泄漏原因 |
|---|---|---|---|
| Activity 泄漏 | detectActivityLeaks() | Activity.onDestroy() 后 GC | 静态引用、匿名内部类持有 Activity |
| 未关闭 Cursor | detectLeakedClosableObjects() | GC 后 finalize 触发 | SQLiteCursor 未 close() |
| 未注销 Receiver | detectLeakedRegistrationObjects() | Context 销毁后 GC | BroadcastReceiver 未 unregister |
| Sqlite 对象泄漏 | detectSqliteObjectLeaks() | GC 后检查 | SQLiteStatement 未释放 |
| 文件描述符超限 | detectFileUriExposure() | FileProvider 未配置 | file:// Uri 暴露 |
| 所有资源 | detectAll() | 综合检测 | 快速启用 |
实战建议: VmPolicy 的 penalty 机制与 ThreadPolicy 不同,它没有 penaltyDeath()(因为泄漏检测是异步且可能误报)。通常搭配 penaltyLog() 在 Logcat 中标记,结合
adb shell dumpsys activity processes 确认泄漏实例。
2.3 职责划分:两种策略如何协同工作
ThreadPolicy 解决性能卡顿问题(ANR 的直接诱因),VmPolicy 解决资源泄漏问题(内存溢出和稳定性隐患)。两者在排查 ANR 时的侧重点不同:
- 主线程慢操作 → ThreadPolicy 直接捕获并输出调用栈。
- 资源泄漏导致 GC 频繁 → VmPolicy 在泄漏发生后示警,间接帮助改善 ANR(因为频繁 GC 会抢占主线程时间)。
2.4 常见配置错误与调试
错误案例1:只配置 ThreadPolicy 忽略 VmPolicy,导致 Cursor 泄漏长期未被发现,最终引发 OOM,而 ANR 日志中只有 GC 信息。
错误案例2:在主线程中调用
错误案例2:在主线程中调用
StrictMode.noteSlowCall() 但未在 Builder 中调用 .detectCustomSlowCalls(),自定义耗时检测不会生效。
调试要点:
- 通过
adb logcat | grep StrictMode快速过滤策略警告。 - VmPolicy 的泄漏警告通常包含“StrictMode VmPolicy violation”标签。
- 使用
StrictMode.allowThreadDiskReads()临时豁免特定代码段(例如在主线程中加载小图片)。
// 临时豁免示例
StrictMode.ThreadPolicy old = StrictMode.allowThreadDiskReads();
try {
Bitmap bmp = BitmapFactory.decodeFile(path);
} finally {
StrictMode.setThreadPolicy(old);
}
2.5 性能影响与部署建议
StrictMode 本身会引入少量性能开销(约 0.1~0.3ms/次检测)。生产环境必须关闭具体方式:通过 BuildConfig.DEBUG 条件编译,或使用 SystemProperties 运行时开关。
部署检查清单:
// Release 构建时自动禁用
if (BuildConfig.DEBUG) {
StrictMode.enableDefaults(); // 或自定义配置
} else {
// 不做任何 StrictMode 初始化
}
两种策略的配置可独立开关,建议在 DEBUG 构建中同时启用 detectAll() + penaltyLog(),待稳定后再选择性增加 penaltyDeath() 加速修复。