第2章:Handler与Activity生命周期绑定链
现象识别
泄漏表象:Activity退出后,Activity.onDestroy()正常执行,但该Activity实例仍被Handler和MessageQueue引用,导致GC无法回收。常见表现为:
- 多旋回泄漏:反复旋转屏幕或跳转退出Activity,Heap中Activity实例数量持续增长。
- 延迟Message滞留:Handler发送了
sendMessageDelayed()(如延迟10秒、30秒),在延迟期间退出Activity,Message仍在队列中,导致泄漏。 - 隐式this泄漏:内部匿名Handler类持有外部Activity的隐式引用(
this$0),链路上无法断开。
判断规则(P0级泄漏):若Activity退出后3分钟,Memory Profiler中仍存在该Activity的强引用链,且根引用指向
android.os.Handler或android.os.MessageQueue,则判定为Handler生命周期绑定链泄漏。数据采集
通过Android Memory Profiler + Dump HPROF采集以下关键数据:
- 抓取Heap快照,筛选
Activity子类实例,确认数量与期望值(应当只有1个)不符。 - 对泄漏的Activity实例执行“到GC根的引用链”分析(Path to GC Roots → exclude weak references)。
- 记录引用链路中的关键节点:
MessageQueue → Message → Handler → Activity。
// 典型泄漏日志(ART GC 日志)
D/dalvikvm: GC_CONCURRENT freed 204K, 3% free 12348K/12640K, paused 2ms+2ms, total 20ms
// 无异常,但Heap中Activity实例数异常增多
// Dump HPROF 后可看到:
// GC Root: Thread (main)
// → MessageQueue (mMessages)
// → Message (target)
// → Handler (mCallback = null)
// → MainActivity (this$0) // 隐式引用
采集清单:
- 泄漏Activity的完整引用链长度(通常≥5级)
- 延迟Message的
when时间戳(判断是否未过期) - Handler发送方式:
sendMessage/postDelayed/sendEmptyMessageDelayed
原因定位
引用链的完整路径为:
- MessageQueue.mMessages(主线程Looper队列)→ Message(持有
target字段指向Handler) - Handler(内部匿名类)→ 隐式this$0(指向Activity实例)
- Activity(持有大量View、Bitmap等资源)
根本原因:非静态内部类或匿名Handler类自动持有外部Activity的引用;当Message尚未被处理时(尤其是延迟消息),该引用链无法切断,导致Activity无法被GC回收。
常见误区:即使没有延迟消息,若Handler在子线程中执行
handleMessage()且子线程未终止,Message可能永远不被处理,造成永久泄漏。修复动作
标准修复策略包含以下四种,按优先级排序:
| 方案 | 操作 | 适用场景 |
|---|---|---|
| 静态Handler + 弱引用 | 声明Handler为static class MyHandler extends Handler,内部使用WeakReference<Activity>访问外部 | 所有需访问Activity的Handler |
| Activity销毁时移除消息 | 在onDestroy()中调用handler.removeCallbacksAndMessages(null) | 延迟消息或重复发送的场景 |
| Lifecycle感知绑定 | 使用LiveData或LifecycleObserver自动取消Handler | AndroidX项目、MVVM架构 |
| 替换为协程/Flow | 用viewModelScope.launch或lifecycleScope替代Handler | Kotlin项目、新开发 |
// 修复示例:静态Handler + 弱引用
public class MainActivity extends AppCompatActivity {
private static class SafeHandler extends Handler {
private final WeakReference activityRef;
SafeHandler(MainActivity activity) {
activityRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = activityRef.get();
if (activity != null && !activity.isFinishing()) {
// 安全操作
}
}
}
private SafeHandler mHandler = new SafeHandler(this);
@Override
protected void onDestroy() {
mHandler.removeCallbacksAndMessages(null); // 额外保险
super.onDestroy();
}
}
复测
修复后进行泄漏复现还原测试:
- 启动Activity → 发送延迟30秒的Message → 立即退出Activity(旋转屏幕或返回)。
- 等待1分钟后,抓取Heap快照,统计Activity实例数(预期剩余0个)。
- 使用
adb shell dumpsys meminfo package_name检查ActivityCount。
通过标准:Activity退出后3次GC(
Runtime.getRuntime().gc())后,Heap中该Activity实例引用链长度为0,Memory Profiler无android.os.Handler强引用指向该Activity。预防
- 编码规范:禁止使用匿名Handler或非静态内部Handler;若必须使用,声明为static并配合WeakReference。
- 静态分析工具:集成Lint规则
HandlerLeak(默认开启)或SpotBugs的DM_HANDLER_LEAK。 - 架构约束:使用Lifecycle-Aware组件(如
LiveData、lifecycleScope)替代原始Handler。 - Review清单:代码审查时检查所有
sendMessageDelayed的调用是否有对应的removeCallbacks。
// 预防Lint配置(build.gradle)
android {
lintOptions {
disable 'HandlerLeak' // 强烈不建议禁用!应修复而非关闭
}
}