Android Handler 内存泄漏分析

引用链分析

假如 Activity 正在执行一个耗时任务 ,(比如handler.sendMessageDelayed(..., 10000),延迟10秒才执行),然后用户按返回键退出了Activity。此时GC要回收Activity,但被这条链卡住了:

➡️ Looper(主线程Looper)

  主线程 的 ThreadLocal 强引用了 Looper(主线程初始化时初始化Looper时被引用),而主线程生命周期跟APP进程一样长,属于GC Root。

➡️ MessageQueue(消息队列)

  Looper持有一个全局的消息队列,只要APP不杀,它就在。

➡️ Message(延迟10秒的那个消息对象)

  这个Message还在队列里排队等待执行,没被取走。

➡️ msg.target(指向Handler,具体发送消息的那个 Handler)

  Message对象内部有一个 target 字段,指向发送它的Handler。这是Android源码机制决定的。

➡️ this(指向MainActivity)

  关键点来了!因为你的Handler是非静态内部类,编译器会偷偷给它加一个成员变量,叫 this$0,专门指向外部的MainActivity实例。(隐式持有

解决方案

错误写法 (非静态内部类 ------ 会泄漏)
java 复制代码
public class MainActivity extends Activity {
    // ❌ 非静态匿名内部类,隐式持有外部MainActivity的引用
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            updateUI(); // 如果耗时任务未完成,Activity无法被回收
        }
    };
}
正确写法
java 复制代码
public class MainActivity extends Activity {

    // 1. 静态内部类,切断对外部类的强引用
    private static class SafeHandler extends Handler {
        // 2. 使用弱引用持有外部类,不阻碍GC回收
        private final WeakReference<MainActivity> mActivityRef;

        SafeHandler(MainActivity activity) {
            mActivityRef = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            // 3. 使用时必须判空!如果Activity已被回收,直接return
            MainActivity activity = mActivityRef.get();
            if (activity == null) {
                return;
            }
            // 安全调用外部类方法
            activity.updateUI();
        }
    }

    private final SafeHandler handler = new SafeHandler(this);

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 4. (加分项)移除所有未处理的消息,彻底阻断泄漏风险
        handler.removeCallbacksAndMessages(null);
    }

    private void updateUI() {
        // 更新界面逻辑
    }
}

关键在于:

  1. 使用 静态内部类声明 Handler 切断隐式引用
  2. 要调用 外部类Activity 的方法,使用 弱引用的方式
    允许 GC 内存紧张时回收,必要时获取外部实例,但是每次使用前需要 get() 并且判空
  3. 生命周期同步清理
    "弱引用只解决了'内存回收'问题,但解决不了'任务残留'问题。如果 Activity 被回收后,延迟消息还在队列里,等它执行时 get() 为 null,业务逻辑就白白浪费了。所以 remove 是为了主动取消无用任务,彻底斩断 Message 对 Handler 的持有,形成完美闭环。"
相关推荐
张小姐的猫1 小时前
【AI大模型接入SDK】 —— Gemini接入封装
android·数据结构·数据库·c++·人工智能·python
Anhty5 小时前
叮咚变声器上手实测,iOS短板、安卓悬浮窗使用感受分享
android·人工智能·功能测试·ios·智能手机
方白羽5 小时前
OkHttp 5.3 隐形变更引发的线上偶发崩溃复盘
android·app·客户端
EatFan6 小时前
【实战经验】uni-app使用 SSE 踩坑,EventSource不支持怎么办?
android·后端·ios·uni-app
JMchen7 小时前
第 2 篇|Kotlin 进阶 —— 集合、循环与条件表达式
android·kotlin
律宏阔7 小时前
Android 自定义 Launcher 加载第三方 App Widget
android
律宏阔7 小时前
Android 9 开发板实现系统侧滑返回
android
2601_962293538 小时前
Appium+python自动化(十二)- Android UIAutomator终极定位凶器(超详解)
android·自动化测试·appium·定位·uiautomator
mmsx8 小时前
MapLibre 自定义比例尺:Web 墨卡托屏幕距离换算公式与实现
android·源码·地图·maplibre