StrictMode Handler 边界
StrictMode 不是 Handler 的消息拦截器,也不会在每条 Message 开始时自动替换 ThreadPolicy。 Android 17 的关系是:ThreadPolicy 绑定当前线程,BlockGuard/StrictMode 检测点在磁盘、网络和 自定义慢调用发生处读取该策略;Handler 只决定这些检测代码在哪个 Looper 线程执行。违规处理 可能同步记录、抛异常、调用 ActivityManager,或把 listener 投递到 Executor。
本文面向已经读过 Looper消息监控、Looper Observer 机制、 消息延迟边界 和 Handler消息处理 的读者。 本文只追踪 ThreadPolicy 与 Handler/Looper 的交界:策略如何安装,检测点如何触发,penalty 如何处理,临时放宽如何恢复,以及 Binder 和 Executor 为什么会改变违规处理线程。
1. 策略归属
源码文件:frameworks/base/core/java/android/os/StrictMode.java
ThreadPolicy 的检测 mask 和 penalty mask 属于线程级 BlockGuard policy;VmPolicy 是进程级策略。 Handler 不拥有 ThreadPolicy,Looper 也不会为每条消息创建新策略。
public static final class ThreadPolicy {
final @ThreadPolicyMask int mask;
final OnThreadViolationListener mListener;
final Executor mCallbackExecutor;
}
private static final ThreadLocal<OnThreadViolationListener> sThreadViolationListener =
new ThreadLocal<>();
private static final ThreadLocal<Executor> sThreadViolationExecutor =
new ThreadLocal<>();Handler 消息运行在目标 Looper 线程已有的策略下;消息跨线程不等于策略跨线程复制。
2. 策略安装
源码文件:frameworks/base/core/java/android/os/StrictMode.java
Builder 把 detect bits 和 penalty bits 组合成 ThreadPolicy;如果设置检测位却没有显式 penalty, build() 会默认启用 penaltyLog()。
public ThreadPolicy build() {
if (mListener == null && mMask != 0
&& (mMask & (PENALTY_DEATH | PENALTY_LOG
| PENALTY_DROPBOX | PENALTY_DIALOG)) == 0) {
penaltyLog();
}
return new ThreadPolicy(mMask, mListener, mExecutor);
}
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectNetwork().penaltyLog().build());策略只影响执行 setThreadPolicy() 的线程。Handler callback 中安装的策略会持续影响同一 Looper 后续消息,直到显式修改或恢复。
3. 检测入口
源码文件:frameworks/base/core/java/android/os/StrictMode.java
检测点读取当前 BlockGuard policy。检测位未打开时直接返回;noteSlowCall() 也不是 Looper 自动调用的慢消息检测,只有代码显式标记且开启 custom slow call 才生效。
public static void noteDiskRead() {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) return;
policy.onReadFromDisk();
}
public static void noteSlowCall(String name) {
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (!(policy instanceof AndroidBlockGuardPolicy)) return;
((AndroidBlockGuardPolicy) policy).onCustomSlowCall(name);
}4. Handler 交界
源码文件:frameworks/base/core/java/android/os/Looper.java
Looper 取消息后直接调用 target Handler,不保存/恢复 StrictMode policy。策略生命周期属于线程 和 callback owner,不属于 Message。
Message msg = me.mQueue.next();
if (msg == null) return false;
msg.target.dispatchMessage(msg);如果 callback 临时放宽 policy 而不在 finally 恢复,后续同一 Looper 的消息会继承放宽后的策略。
5. 网络检测
源码文件:frameworks/base/core/java/android/os/StrictMode.java
onNetwork() 在检测位开启时,如果配置 PENALTY_DEATH_ON_NETWORK 会直接抛 NetworkOnMainThreadException;否则建立 ViolationInfo 并进入 penalty 流程。
public void onNetwork() {
if ((mThreadPolicyMask & DETECT_THREAD_NETWORK) == 0) return;
if ((mThreadPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
throw new NetworkOnMainThreadException();
}
if (tooManyViolationsThisLoop()) return;
startHandlingViolationException(new NetworkViolation());
}在 Handler 里执行网络不天然违规;要同时满足当前线程 policy 开启检测和 penalty 配置条件。
6. 违规计时
源码文件:frameworks/base/core/java/android/os/StrictMode.java
违规处理记录发生时间,再尝试使用当前 Looper 估算 duration。没有 Looper 或只配置 death 时, 不能使用 Looper 计时。
void startHandlingViolationException(Violation e) {
final int penaltyMask = (mThreadPolicyMask & PENALTY_ALL);
final ViolationInfo info = new ViolationInfo(e, penaltyMask);
info.violationUptimeMillis = SystemClock.uptimeMillis();
handleViolationWithTimingAttempt(info);
}
void handleViolationWithTimingAttempt(final ViolationInfo info) {
Looper looper = Looper.myLooper();
if (looper == null || info.mPenaltyMask == PENALTY_DEATH) {
info.durationMillis = -1;
onThreadPolicyViolation(info);
return;
}
// 使用当前 Looper/MessageQueue 估算违规持续时间
}报告中的 duration 不是 Handler dispatch 时长的同义词;它可能覆盖违规发生后直到 Looper 再次 空闲的区间。
7. Penalty 线程
源码文件:frameworks/base/core/java/android/os/StrictMode.java
penalty listener 使用调用者提供的 Executor。源码会在 listener 运行前允许线程违规,运行后 恢复旧策略,防止上报本身递归触发 StrictMode。
final OnThreadViolationListener listener = sThreadViolationListener.get();
final Executor executor = sThreadViolationExecutor.get();
if (listener != null && executor != null) {
executor.execute(() -> {
ThreadPolicy oldPolicy = StrictMode.allowThreadViolations();
try {
listener.onThreadViolation(violation);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
});
}检测线程和上报线程可以不同:Executor 可能是 HandlerExecutor,也可能是 worker pool。
8. Binder 传播
源码文件:frameworks/base/core/java/android/os/StrictMode.java
StrictMode 可以在 Binder 边界传播违规信息,但类注释明确这是 best effort,JNI 发起的磁盘/网络 访问不一定触发。接收方读取 Parcel 后,再按自己的 BlockGuard policy 处理。
public static void readAndHandleBinderCallViolations(Parcel p) {
Throwable localCallSite = new Throwable();
final int policyMask = getThreadPolicyMask();
final boolean gathering = (policyMask & PENALTY_GATHER) != 0;
final int size = p.readInt();
for (int i = 0; i < size; i++) {
final ViolationInfo info = new ViolationInfo(p, !gathering);
info.addLocalStack(localCallSite);
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
if (policy instanceof AndroidBlockGuardPolicy) {
((AndroidBlockGuardPolicy) policy).handleViolationWithTimingAttempt(info);
}
}
}这不是 Handler 转发:Binder 传播的是违规信息和调用栈,接收线程再按自己的策略处理。
9. 策略恢复
源码文件:frameworks/base/core/java/android/app/ActivityThread.java
framework 临时放宽 policy 的调用点保存旧值,并在 finally 恢复。Handler callback 若需要同样 操作,也必须自己保证所有异常路径回到旧策略。
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.setThreadPolicy(policy);
// 允许的系统操作
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}Looper 不会替业务恢复;策略泄漏到下一条消息会造成检测盲区。
10. Looper 配置
源码文件:frameworks/base/core/java/android/os/Looper.java
设置 Message logging 本身显式调用 StrictMode.noteSlowCall("setMessageLogging"),但这不表示 Looper 每次 dispatch 都自动调用 custom slow call。
public void setMessageLogging(@Nullable Printer printer) {
if (printer != null) StrictMode.noteSlowCall("setMessageLogging");
mLogging = printer;
}Handler 性能日志、StrictMode custom slow call 和 Looper slow dispatch 是三个不同观测点。
11. 线程测试
源码文件:frameworks/base/core/java/android/os/StrictMode.java
可执行验证应在受控 HandlerThread 上安装不同 ThreadPolicy,分别投递调用 noteSlowCall() 的 Runnable,断言违规记录对应执行线程的 policy;再用 HandlerExecutor 和 worker Executor 作为 penalty listener executor,记录 listener 实际线程。
主 Looper policy A + Handler.post(taskA) -> taskA 使用 policy A
worker Looper policy B + Handler.post(taskB) -> taskB 使用 policy B
违规 listener Executor C -> 上报在 C 的线程执行这个实验不证明 JNI I/O 一定被检测,也不证明 listener 在检测线程执行。
12. 复查命令
源码文件:
frameworks/base/core/java/android/os/StrictMode.javaframeworks/base/core/java/android/os/Looper.javaframeworks/base/core/java/android/app/ActivityThread.javaframeworks/base/core/java/android/os/HandlerExecutor.java
rg -n "setThreadPolicy|detectDiskReads|detectNetwork|detectCustomSlowCalls|penaltyListener" \
frameworks/base/core/java/android/os/StrictMode.java
rg -n "noteSlowCall|noteDiskRead|noteDiskWrite|onNetwork|handleViolationWithTimingAttempt" \
frameworks/base/core/java/android/os/StrictMode.java
rg -n "setMessageLogging|StrictMode.noteSlowCall|setThreadPolicy\(" \
frameworks/base/core/java/android/os/Looper.java \
frameworks/base/core/java/android/app/ActivityThread.java13. 适用边界
StrictMode 适合发现线程上的磁盘、网络和 custom slow call 违规;Handler 只决定违规代码在哪个 Looper 线程运行。它不是安全机制,也不保证捕获所有 JNI/Native 访问,更不提供自动迁移工作。
排查 Handler 中的违规时,先确认当前线程 policy 和检测位,再确认检测点是否经过 BlockGuard/StrictMode,最后追踪 penalty 是同步执行、交给 Executor,还是通过 Binder 传播。 临时放宽策略时保存并恢复旧 policy,避免一条消息改变后续整个 Looper 的检测行为。
