Android --- Handler 内存泄漏原因及解决方案

一、原因

Handler造成内存泄露的原因。非静态内部类,或者匿名内部类。使得Handler默认持有外部类的引用。在Activity销毁时,由于Handler可能有未执行完/正在执行的Message。导致Handler持有Activity的引用。进而导致GC无法回收Activity。

二、可能造成内存泄漏

匿名内部类:

//匿名内部类  
Handler handler=new Handler(){
  @Override
  public void handleMessage(Message msg) {
    super.handleMessage(msg);
  }
};

非静态内部类:

  //非静态内部类
  protected class AppHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
      switch (msg.what) {
        // TODO: 2019/4/30 
      }
    }
  }

三、解决方法

静态内部类+弱引用

private static class MyHandler extends Handler{
	private final WeakReference<MineActivity> mMineActivityWeak;
	public MyHandler(MineActivity mineActivity){
		mMineActivityWeak = new WeakReference<>(mineActivity);
	}
	@Override
	public void handleMessage(@NonNull Message msg) {
		super.handleMessage(msg);
		MineActivity mineActivity = mMineActivityWeak.get();
		if(mineActivity != null){
			mineActivity.number = 5;
        }
    }
}

Activity销毁时,清空Handler中,未执行或正在执行的Callback以及Message。

  // 清空消息队列,移除对外部类的引用
  @Override
  protected void onDestroy() {
    super.onDestroy();
    mHandler.removeCallbacksAndMessages(null);

  }

 
  //Handler源码中removeCallbacksAndMessages()注释含义
  /**
   * Remove any pending posts of callbacks and sent messages whose
   * <var>obj</var> is <var>token</var>. If <var>token</var> is null,
   * all callbacks and messages will be removed.
   */
  public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
  }

四、内部类为什么会持有外部类的引用

这是因为内部类虽然和外部类写在同一个文件中,但是编译后还是会生成不同的class文件,其中内部类的构造函数中会传入外部类的实例,然后就可以通过this$0访问外部类的成员。

其实也挺好理解的吧,因为在内部类中可以调用外部类的方法,变量等等,所以肯定会持有外部类的引用的。

贴一段内部类在编译后用JD-GUI查看的class代码,也许你能更好的理解:

//原代码
class InnerClassOutClass{
 
    class InnerUser {
       private int age = 20;
    }
}
 
//class代码
class InnerClassOutClass$InnerUser {
    private int age;
    InnerClassOutClass$InnerUser(InnerClassOutClass var1) {
        this.this$0 = var1;
        this.age = 20;
     }
}

那为什么静态内部类不持有外部类引用呢?你可以自己试试写一个静态内部类,外部再写几个成员变量,你看在静态内部类中能不能调到,调不到就是不持有外部类的引用

使用弱引用的一个常见目的是为了访问外部类的成员变量。
当我们在静态内部类中需要访问外部类的成员变量时,可以使用弱引用来解决。弱引用允许在没有其他强引用指向对象时,对象可以被垃圾回收器回收。

五、Runable 的内存泄漏解决方案

public class MineFragment extends Fragment {
	private RecycleView reclcleView;
	
	private static class PositionRunnable implements Runnable {
		private final int lastPosition;
		private final int lastOffset;
		private WeakReference<MineFragment> mineFragmentWeak;
		public PositionRunnable(MineFragment mineFragment, int lastPosition, int lastOffset) {
			this.mineFragmentWeak = new WeakReference<>(mineFragment);
			this.lastPosition = lastPosition;
			this.lastOffset = lastOffset;
		}
		@Overried
		public void run() {
			mineFragmentWeak.get().reclcleView.scrollToPosition(lastPosition);
		}
	}
	
	// 用的地方直接
	PositionRunnable pRunnable = new PositionRunnable(this, 100, 200);
	reclcleView.post(pRunnable);
}

部分内容来自:https://www.jb51.net/article/205828.htm

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梁同学与Android

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值