Android中Handler使用不当导致内存泄露的问题

来源:互联网 发布:淘宝口令怎么用优惠券 编辑:程序博客网 时间:2024/05/05 22:32

参考:
http://www.linuxidc.com/Linux/2013-12/94065.htm
http://www.cnblogs.com/fanglove/archive/2013/03/08/2950102.html

在使用Handler时,通常是new一个内部类(或匿名内部类):

private Handler mHandler=new Handler(){    public void handleMessage(android.os.Message msg) {        //Update UI...    };};

这时会发现编译器给了个Warning:This Handler class should be static or leaks might occur

这是因为ADT20以后增加了这样一个变化:
New Lint Checks: Look for handler leaks: This check makes sure that a handler inner class does not hold an implicit reference to its outer class.
翻译过来就是:
Lint会增加一个检查项目即:确保class内部的handler不含有外部类的隐式引用 。

还以上面那段简单的代码为例,当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。

这么一来,编译器的Warning就好理解了:我们把Handler定义为static,就是为了切断Handler和外部类的联系,这样在外部类对象被GC回收的时候,不会因为Handler无法回收而影响外部类对象的回收。

那么问题又来了,既然现在Handler和外部类没有关系了,怎么去操作外部类中的对象呢?有两种方法:

1.用Handler的post方法把Runnable对象post到主线程,添加到MessageQueue中。

private static Handler handler;public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    handler = new Handler();    //Create a handler to update the UI}void test() {   //在新线程中调用该方法    handler.post(new MyRunnable());     //也可以用runOnUiThread(new MyRunnalble());来替换,效果是一样的。}static public class MyRunnable implements Runnable {    @Override    public void run() {    //Update UI...    }}

可能有人会说了,这种方法需要把每个消息都封装成对应的Runnable对象post到主线程,没有原来一个Handler处理多个Message的方法来的简单,不过不要紧,我们还有第二种方法。

2.让Handler持有Activity的弱引用。这样以来,在GC回收Activity时,发现Handler持有Activity的弱引用,仍可以将Activity回收掉。

MyHandler mHandler = new MyHandler(this);   //传入Activitystatic class MyHandler extends Handler {    WeakReference<PopupActivity> mActivity;    MyHandler(PopupActivity activity) {    //构造方法传入Activity,并生成弱引用        mActivity = new WeakReference<MyActivity>(activity);    }    @Override    public void handleMessage(Message msg) {        switch(msg.what){        case 0:            //Update UI...            break;        case 1:            //Update UI...            break;        //...        }    }}private void test() {   //在新线程中调用    mHandler.sendEmptyMessage(0);}

写在最后:一般来说,Handler不定义为static并不会出现太大的问题,postDelayed()方法一般也没什么问题,但在一些特殊的情况下(参考http://www.cnblogs.com/leftwing/archive/2012/02/09/2343420.html),比如postDelayed()嵌套,是肯定会造成内存泄露的。希望以后在写程序时注意这个问题,从小事做起。

0 0