如何看待Handler使用中的警告

本文探讨了Android中Handler使用不当导致的内存泄漏问题,并提供了两种解决方案:一是通过静态内部类及弱引用防止泄漏;二是清除消息队列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何看待Handler使用中的警告

相信很多时候,我们都可以看到自己在使用Handler的过程中,好像得到了一个warning,虽然只是一个警告,但这总是让强迫症的我们有那么一丝丝不爽。那么应该如何解决这样的警告呢?

如图可见:

bcaWd.png

可以看到提示信息:

This Handler class should be static or leaks might occur

[这个Handler类应该是静态的,否则可能会发生内存泄漏]

对于我们严谨的开发者,应该使用Handler才是一种更好的体验呢?

内存泄漏的可能性原因

为什么这样子使用的时候会可能产生内存泄漏呢?

因为这样子的使用过程中,其实我们重写了Handler的内部方法并生成了一个匿名内部类,而且由于创建匿名内部类或者内部类的实例会默认持有外部类对象的引用

一种情况:当我们在Activity这样子使用的时候,如果在执行一个耗时任务,当任务未执行完毕的时候,Activity被关闭了,Activity已不再使用,此时如果回收Activity对象,由于子线程未执行完毕,子线程持有Handler的引用,而Handler又持有Activity的引用,这样导致Activity对象无法被回收,即出现内存泄漏。

另一种情况:同样是Activity异常关闭,这时候Handler消息传递机制中还存在未消耗的Message,也就是MessageQueue中还有Message没有处理,这个时候也可能会发生内存泄漏,但是这个情况没有更多的解释。因为Handler是基于消息的。每次new 出Handler,都会创建一个消息队列用于处理你使用handler发送的消息,形如:handler.send***Message。由于消息的发送总是会有先来后到的区别(如果只是这样都还好,毕竟再慢也不会太久,总归可以跑完,可能会延迟个几秒),但是如果你使用的是sendMessageDelayed(Message msg, long delayMillis)或postDelayed(Runnable r, long delayMillis)等发送延迟消息的时候,那基本内存泄漏发生的概率已经在90%以上了。

那么如何解决这个问题呢?

有一位攻城狮RomainGuy(Android Framework)给出了一些建议。链接:

https://groups.google.com/forum/?fromgroups=#%21msg/android-developers/1aPZXZG6kWk/lIYDavGYn5UJ

如下为截图部分:

截图

另外,在androiddesignpatterns上也有人给出了解决方案:
http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html

解决方法主要在于两点:
1.将Handler声明为静态内部类。因为静态内部类不会持有外部类的引用,所以不会导致外部类实例出现内存泄露。
2.在Handler中添加对外部Activity的弱引用。由于Handler被声明为静态内部类,不再持有外部类对象的引用,导致无法在handleMessage()中操作Activity中的对象,所以需要在Handler中增加一个对Activity的弱引用。

改善之后的代码如下:

private static class MyHandler extends Handler {  
        private final WeakReference<MainActivity> mActivity;  

        public MyHandler(MainActivity activity) {  
            this.mActivity = new WeakReference<MainActivity>(activity);  
        }  

        @Override  
        public void handleMessage(Message msg) {  
            MainActivity mainActivity = mActivity.get();  
            if (mainActivity == null) {  
                return;  
            }  
            // your code here  
        }  
    }  

WeakReference即弱引用,与强引用(即我们常说的“引用”)相对。它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向,该对象就会在被GC检查到时回收掉。

改为采用WeakReference之后,如果任务未执行完但是用户却关闭了MainActivity,但由于仅有一个来自MyHandler的弱引用指向MainActivity,所以GC仍然会在检查时把MainActivity回收掉。这样,内存泄露的问题就不会出现了。

另一种方案

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

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

  所以,我觉得最好的办法就是你在使用Handler的时候,在外面的Activity或者Fragment中的关闭方法中,如onDestroy中调用一下handler.removeCallbacksAndMessages(null);就可以了,不应该改成软引用。

示例代码:

    Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    })

最后在外部类的onDestory方法中插入如下代码:

mHandler.removeCallbacksAndMessages(null);

总结

对比两种方案,第一种方案需要一个静态内部类,第二种方案只需要重写一个Callback接口,而且没有可能出现内存泄漏的提示,至于那个较好,这个没有实际的测试,看大家了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值