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

文章目录

  • 一、原因
  • 二、可能造成内存泄漏
  • 三、解决方法
  • 四、内部类为什么会持有外部类的引用
  • 五、Runable 的内存泄漏解决方案

一、原因

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
   * obj is token. If token 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

你可能感兴趣的:(Android,项目开发笔记,android,java,jvm)