有效的避免Handler的泄漏

本文围绕Android系统中的Handler展开,介绍其是消息发送和处理机制的核心组件,与Looper、Message、MessageQueue配套。阐述了Handler的两个主要作用,分析了其可能造成内存泄漏的原因,即内部类隐式持有外部类引用,还给出了相应的解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

查找和整理相关和记录相关知识点,复盘,方便一起巩固。

在Android系统中,Handler是一个消息发送和处理机制的核心组件之一,与之配套的其他主要组件还有Looper和Message,MessageQueue。
Message和Runnable类是消息的载体。MessageQueue是消息等待的队列。Looper则负责从队列中取消息。

Handler有两个主要作用:

  • 1.安排调度(scheule)消息和可执行的runnable,可以立即执行,也可以安排在某个将来的时间点执行。

  • 2.让某一个行为(action)在其他线程中执行。

Handler是由系统所提供的一种异步消息处理的常用方式,一般情况下不会发生内存泄露。

Handler为什么可能造成内存泄漏。这里的内存泄漏,常常指的是泄漏了Activity等组件。

public class ShanActivity extends Activity{
    public Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
             
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
	}
}

这有什么问题呢。问题在于该Handler的实例采用了内部类的写法,它是ShanActivity这个实例的内部类,在Java中,关于内部类有一个特点:在java中,非静态的内部类和匿名内部类都会隐式的持有一个外部类的引用。所以,该handler实例持有了ShanActivity的一个引用。

生命周期较短的组件引用了生命周期较长的组件。Handler就是一种典型的示例,以上面的代码举例。ShanActivity可能会被泄漏,也就是该组件没有用了,比如调用了finish()后,垃圾回收器却迟迟没有回收该Activity。原因出在该实例的handler内部类引用了它,而该handler实例可能被MessageQueue引用着。

从上面的说法中,可以思考得到相应的解决方法:

  • 1.保证Activity被finish()时该线程的消息队列没有这个Activity的handler内部类的引用。这个场景是及其常见的,因为handler经常被用来发延时消息。一个补救的办法就是在该类需要回收的时候,手动地把消息队列中的消息清空:mHandler.removeCallbacksAndMessages(null);

  • 2.要么让这个handler不持有Activity等外部组件实例,让该Handler成为静态内部类。(静态内部类是不持有外部类的实例的,因而也就调用不了外部的实例方法了)

  • 3.在2方法的基础上,为了能调用外部的实例方法,传递一个外部的弱引用进来)

  • 4.将Handler放到抽取出来放入一个单独的顶层类文件中。

这里需要了解一下关于Java里面引用的知识:

强引用(Strong Reference)默认引用。如果一个对象具有强引用,垃圾回收器绝不会回收它。在内存空 间不足时,Java虚拟机宁愿抛出OutOfMemory的错误,使程序异常终止,也不会强引用的对象来解决内存不足问题。
软引用(SoftReference)如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
弱引用(WeakReference)在垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference)如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

第三种,需要一些额外的代码,比较通用。

public class ShanActivity extends Activity {
private static class MyHandler extends Handler {
private final WeakReference<ShanActivity> mActivity;
public MyHandler(ShanActivity activity) {
  mActivity = new WeakReference<ShanActivity>(activity);
}

@Override
public void handleMessage(Message msg) {
  ShanActivity activity = mActivity.get();
  if (activity != null) {
     //do Something
  }
}
}

第四种方式,抽取做单独封装。

/**
 * 实现回调弱引用的Handler
 * 防止由于内部持有导致的内存泄露
 * 传入的Callback不能使用匿名实现的变量,必须与使用这个Handle的对象的生命周期一 
 * 致否则会被立即释放掉了
 */
public class WeakRefHandler extends Handler {
    private WeakReference<Callback> mWeakReference;
    
    public WeakRefHandler(Callback callback) {
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    public WeakRefHandler(Callback callback, Looper looper) {
        super(looper);
        mWeakReference = new WeakReference<Handler.Callback>(callback);
    }
    
    @Override
    public void handleMessage(Message msg) {
        if (mWeakReference != null && mWeakReference.get() != null) {
            Callback callback = mWeakReference.get();
            callback.handleMessage(msg);
        }
    }
}

由于是弱引用,当该类需要被回收时,可以直接被回收掉。

WeakRefHandler的使用时如下:

    private Handler.Callback mCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch(msg.what){
            }
            return true;
        }
    };
    private Handler mHandler = new WeakRefHandler(mCallback);
<think>好的,用户想解决AndroidHandler导致的内存泄漏问题,需要分析原因和解决方案。首先,我得回忆一下HandlerAndroid中的工作机制。Handler通常用于线程间通信,尤其是在主线程处理UI更新。如果Handler被声明为非静态内部类,它会持有外部Activity的隐式引用。当Activity销毁时,如果Handler还有未处理的消息,就会导致Activity无法被回收,从而引发内存泄漏。 接下来,需要整理常见的内存泄漏场景。比如,Handler发送了延迟消息,而Activity在消息处理前就被关闭了,这时MessageQueue持有Handler的引用,而Handler又持有Activity的引用,导致Activity无法释放。这种情况需要如何处理呢? 解决方案方面,我记得有几种方法。第一种是使用静态内部类加弱引用。静态内部类不会持有外部类的引用,再通过弱引用指向Activity,这样在GC时,如果Activity需要回收,弱引用不会阻止。同时,需要在Activity销毁时移除所有回调消息,避免Handler继续执行。 第二种是确保在Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null),这样可以清空消息队列,防止残留的消息导致泄漏。这一点用户可能容易忽略,需要强调。 另外,可能还有其他方法,比如使用Android框架提供的Lifecycle组件,或者第三方库如RxJava来处理异步任务,避免直接使用Handler。不过用户可能更关注原生的解决方案,所以重点放在前两种方法上。 还需要注意,用户提供的引用中提到了内存泄漏的场景是反复打开关闭Activity,导致内存累积。因此,解决方案必须有效切断Handler与Activity之间的引用链,并在适当的时候移除消息。 最后,要检查是否有遗漏的情况,比如匿名内部类Handler同样会导致泄漏,或者使用不当的Thread和Looper没有正确退出。但根据用户的问题,主要集中在Handler本身,所以先集中讨论静态内部类和移除消息的方法。 总结起来,解决方案的核心是避免Handler持有Activity的强引用,并及时清理消息队列。这样就能有效防止内存泄漏的发生。</think>### Android Handler内存泄漏解决方案分析 #### 一、内存泄漏原因 1. **隐式持有Activity引用**:当Handler声明为非静态内部类时,会隐式持有外部类(如Activity)的引用。若Handler未处理完消息时Activity被销毁,会导致Activity无法被垃圾回收[^2]。 2. **消息队列未清空**:若Handler发送了延迟消息(如`postDelayed`),在消息未处理前销毁Activity,MessageQueue会持续持有Handler的引用链: $$ \text{MessageQueue} \rightarrow \text{Handler} \rightarrow \text{Activity} $$ 从而阻止Activity回收[^1]。 --- #### 二、解决方案 ##### 1. 静态内部类 + 弱引用 ```java private static class SafeHandler extends Handler { private final WeakReference<Activity> mActivityRef; SafeHandler(Activity activity) { mActivityRef = new WeakReference<>(activity); } @Override public void handleMessage(@NonNull Message msg) { Activity activity = mActivityRef.get(); if (activity == null || activity.isDestroyed()) { removeCallbacksAndMessages(null); return; } // 处理消息逻辑 } } ``` ##### 2. 及时移除消息 在Activity销毁时清空消息队列: ```java @Override protected void onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null); // 移除所有消息 } ``` ##### 3. 使用Lambda避免匿名类(API 24+) ```java Handler handler = new Handler(Looper.getMainLooper(), msg -> { // 处理消息 return true; }); ``` --- #### 三、验证方法 1. 使用Android Profiler监控Activity销毁后的内存占用 2. 启用LeakCanary检测内存泄漏 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值