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

参考:
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);   //传入Activity

static 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值