Android - Handler内存泄漏与分析

一、Android Handler四件套:Looper + MessageQueue + Message + Handler

  1、Android应用程序启动时,Framework会为应用程序的主线程创建一个Looper对象,该对象包含一个消息队列MessageQueue,并且循环处理队列中的消息Message

  2、这些消息包括大多数应用程序framework事件,如 Activity生命周期方法调用、Button点击等;

  3、在主线程中实例化一个Handler对象,它就会自动与主线程Looper关联(发送的消息通过该Looper引用添加入消息队列中),同时消息Message都会拥有一个Handler的引用(当Looper处理该消息时,会据此回调handleMessage方法);

  4、主线程Looper对象伴随应用程序的整个生命周期;

二、静态内部类/匿名类

  1、非静态内部类和匿名类都会潜在的引用它们所属的外部类,但是,静态内部类却不会;

  2、静态匿名类

  /**
   * Instances of anonymous classes do not hold an implicit
   * reference to their outer class when they are "static".
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

 

三、对象引用类型

  1、强引用 strong :Object object=new Object(). 当内存不足时,JVM宁愿抛出OOM内存溢出异常,也不会回收强引用对象来解决内存不足问题;

  2、软引用 soft :只有内存达到某个阈值才进行回收,常用于缓存;

  3、弱引用 weak :只要被GC线程扫描到就进行回收;

  如果想避免OOM发生,则使用软引用对象,即当内存不足时进行回收;

  如果想尽快回收某些占用内存较大的对象,可以使用弱引用;

四、Handler引发的内存泄漏

  1、引发内存泄漏的不合格代码:

public class SampleActivity extends Activity {

  private final Handler mLeakyHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for 10 minutes.
    mLeakyHandler.postDelayed(new Runnable() {
      @Override
      public void run() { /* ... */ }
    }, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
  }
}

  当activity结束(finish)时, 延时消息需要10分钟后才可以得到处理, 由上文可知, 这条消息持有对handler的引用, handler又持有其外部类(在这里, 即SampleActivity)的潜在引用

  这条引用关系一直保持到消息得到处理, 从而阻止了SampleActivity被GC回收

  注意, 上面代码中的Runnable匿名类同样也持有其外部类的潜在引用。

  2、解决方案:

    1) 使用静态内部类与静态匿名类, 因为它们都不会持有其外部类的引用;

    2) 若需要在Handler内部调用Activity, 可以使用弱引用方式;

    3) Activity的OnDestroy中取消未执行的消息, 因为此时Activity处于不可见状态了, 继续修改视图操作已经没有意义;

  3、改进后合格代码: 

public class SampleActivity extends Activity {

	/**
	* Instances of static inner classes do not hold an implicit
	* reference to their outer class.
	*/
	private static class MyHandler extends Handler {
		private final WeakReference<SampleActivity> mActivity;

		public MyHandler(SampleActivity activity) {
			mActivity = new WeakReference<SampleActivity>(activity);
		}

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

	private final MyHandler mHandler = new MyHandler(this);

	/**
	* Instances of anonymous classes do not hold an implicit
	* reference to their outer class when they are "static".
	*/
	private static final Runnable sRunnable = new Runnable() {
		@Override
		public void run() { /* ... */ }
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// Post a message and delay its execution for 10 minutes.
		mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

		// Go back to the previous Activity.
		finish();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		mHandler.removeCallbacksAndMessages(null);
	}
}

 

五、本文重点:

  非静态内部类/匿名类会持有外部类的引用
  静态内部类/匿名类不会持有外部类的引用
  静态匿名类例子:
  private static final Runnable sRunnable=new Runnable(){
    @override
    public void run(){/*...*/}
  }

  Handler引起的内存泄漏解决方法:
    使用静态内部类/匿名类
    使用weakReference引用Activity
    Activity的onDestroy中取消所有未处理消息:mHandler.removeCallbacksAndMessages(null);

 

  

参考文章:

Android之Handler内存泄漏分析及解决

  http://www.jianshu.com/p/cb9b4b71a820

  http://blog.csdn.net/card361401376/article/details/51493352

在Activity中使用Thread导致的内存泄漏

  https://github.com/hehonghui/android-tech-frontier/tree/master/issue-7/在Activity中使用Thread导致的内存泄漏

Android 背景作業處理

  http://blog.kent-chiu.com/2012/03/19/background_processing.html

  

 

  

 

  

  

posted @ 2016-06-04 17:11  chenyizh  阅读(196)  评论(0)    收藏  举报