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

但既然是调优,当在A_Activity中使用handler发送了几个消息,然后又跳到B_Activity,这个时候如果我们想结束之前在A_Activity中发出的消息,不再占用多余的队列空间.怎么办呢?

原来,Handler中已经提供了一个removeCallbacksAndMessages去清除Message和Runnable:


1. handler.removeCallbacksAndMessages(null);


将方法参数设为null.

在Activity的销毁方法onDestroy()中调用该方法即可.

题外话:

熟悉Activity的生命周期是能让应用在性能调优时达到更好的实现方式. 比如onDestroy() 方法,onPause()方法等等, Activity销毁前生命周期会进入onDestroy(),当前Activity所用过且不再需要的占用内存的资源可以在这里进行一些处理, 比如将不再使用的视图资源清除掉,将正在访问而不再需要的网络连接给终止掉,将消息队列中不再需要的消息清除等等.这样当不断打开新的页面Activity,也能将应用多余的内存缓存尽量减少一些达到调优的目的.

-------------------------

Android开发经常会用到handler,但是我们发现每次使用Handler都会出现:This Handler class should be static or leaks might occur(null)这样的提示。Android lint就是为了提示我们,这样使用Handler会容易造成内存泄漏。但是你会发现其实改成static并没有什么用。因为这并没有解决这个问题的根本。

  首先,我们得确认,为什么会有内存泄漏?因为Handler是基于消息的。每次new 出Handler,都会创建一个消息队列用于处理你使用handler发送的消息,形如:handler.send***Message。由于消息的发送总是会有先来后到的区别(如果只是这样都还好,毕竟再慢也不会太久,总归可以跑完,可能会延迟个几秒),但是如果你使用的是sendMessageDelayed(Message msg, long delayMillis)或postDelayed(Runnable r, long delayMillis)等发送延迟消息的时候,那基本内存泄漏发生的概率已经在90%以上了。

     我举个通常的例子,就是我们在Activity中使用handler来更新UI控件,这是比较常见的。



1 public class DemoActivity extends Activity {
 2 
 3     private Handler mHandler; 
 4 
 5     protected void onCreate(Bundle savedInstanceState) {
 6         mHandler = new Handler();
 7 
 8      mHandler.postDelayed(new Runnable() {
 9        Log.i("wytings","-----------postDelayed-------");
10             view.setVisibility(View.GONE);
11         }, 50000);
12         ...
13     }
14




      如果我们疯狂的对这个Activity进行横屏和竖屏切换的话,那么Activity就会不断的被销毁和重建。理论上被关闭的Activity应该会再特定时候被回收,也就是我们的内存会在一定的范围内上下起伏,但是实际上,会发现消耗的内存会随着切换横屏的次数一直慢慢增加。这其实已经说明我们的内存泄漏了,如果你会查看内存,你会发现里面有成堆的DemoActivity实例没办法回收。

  这是因为view中使用的Context就是当前的Activity,而这个runnable一旦被post,就会一直存在于队列里面,直到时间到了,被执行。意思是这个时间段内Activity即使已经被destroy了但是这个对象还是没办法回收,你会发现50秒好,会有一堆"-----------postDelayed-------"的log打印出来,虽然你已经被这个应用关闭了并且你以为即使打印也应该只打印一次……

  那怎么样才可以避免这中问题呢,如果你网上一搜你会看到很多关于弱引用的文章。这确实是一个解决的办法。其原理就是让所有在handler里面使用的对象都变成弱引用,目的就是为了可以在Android回收内存的时候,可以直接回收掉。我真觉得如果只是写这种办法的人,绝对是属于拷贝党,因为这完全是就事论事。你想想就明白,我们写这个Handler是因为我们要使用它。怎么可以通过这种弱引用的办法去处理这类问题呢?让JVM想回收就回收?!如果这样,那我们还需要在使用Bitmap的时候,recycle()干嘛,还不如直接弄成软引用得了。

这里需要再插播一下关于Java里面引用的知识:

强引用(Strong Reference)

默认引用。如果一个对象具有强引用,垃圾回收器绝不会回收它。在内存空 间不足时,Java虚拟机宁愿抛出OutOfMemory的错误,使程序异常终止,也不会强引用的对象来解决内存不足问题。

软引用(SoftReference)

如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。

弱引用(WeakReference)

在垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

虚引用(PhantomReference)

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

 

handler.removeCallbacksAndMessages(null);,就是移除所有的消息和回调,简单一句话就是清空了消息队列。注意,不要以为你post的是个Runnable或者只是sendEmptyMessage。你可以看一下源码,在handler里面都是会把这些转成正统的Message,放入消息队列里面,所以清空队列就意味着这个Handler直接被打成原型了,当然也就可以回收了。

handler.removeCallbacksAndMessages(null);就可以了,不应该改成软引用。