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 的持有,形成完美闭环。"
相关推荐
其实防守也摸鱼3 小时前
Fastjson 反序列化漏洞(JNDI注入)
android·数据库·安全·oracle·自动化·fastjson·反序列化
狗凯之家源码网4 小时前
微博图床 PHP 系统搭建与实战应用指南
android·开发语言·php
消失的旧时光-19435 小时前
(第一篇)JNI 多线程到底难在哪:从 Linux Thread 到 JNIEnv
android·jni·ndk
逗脑IDE8 小时前
零基础学ESP32:蓝牙控制舵机——手机一点,舵机就转!
android·智能手机
清霜之辰8 小时前
骁龙 8397 舱驾融合平台端侧大模型部署:7B 模型本地推理与 64GB 内存账本
android
烽火聊员8 小时前
Android屏幕卡顿traces.txt拉取查看
android·java·android studio
只睡四小时10 小时前
JS 手写 D-pad 空间导航:电视端焦点引擎实战
android·开发语言·前端·javascript·ecmascript·hls·空间导航
恋猫de小郭10 小时前
Android 原生的 Compose A2UI 也来了,你还抱着 XML 养老吗?
android·前端·flutter
程序媛_10 小时前
【JMeter】准备token文件
android·jmeter
维克兜率天10 小时前
【维克】弹性策略:用“乖离率“捕捉超跌反弹
android·开发语言·python·深度学习·kotlin·量化