android Handler导致的内存泄露的原因及解决办法

在Android开发中,内存泄露十分常见。
内存泄露:本该被回收的对象不能被回收而停留在堆内存中占用内存。这样最后可能会引起内存不足导致其他需要使用内存的对象无法获取足够的内存,进而引起内存溢出OOM。
内存泄露出现的原因:当一个对象已经不再被使用时,本该被回收但却因为有另外一个正在使用的对象持有它的引用从而导致它不能被回收。这就导致了内存泄漏。
Handler是我们在更新UI时经常使用到的类,但是不注意的话,很容易就导致内存泄露,最后导致OOM。现在探究下Handler导致内存泄露的原因及解决办法。

先看下面的代码:

图片代码标黄的地方提示:

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less... (Ctrl+F1) 
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object

简单翻译下:由于handler定义为内部类,可能会阻止GC。如果handler的Looper或MessageQueue 非主线程,那么没有问题。如果handler的Looper或MessageQueue 在主线程,那么需要按如下定义:定义handler为静态内部类,当你实例化handler的时候,传入一个外部类的弱引用,以便通过弱引用使用外部类的所有成员。

1、先说handler导致activity内存泄露的原因:

handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),我们知道内部类天然持有外部类的实例引用,那么就会导致activity无法回收,进而导致activity泄露。

2、为何handler要定义为static?

因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露。

3、为何handler要定义为static的同时,还要用WeakReference 包裹外部类的对象?

如果需要使用外部类的成员,可以通过"activity. "获取变量方法等,如果直接使用强引用,显然会导致activity泄露。当然如果不需要引用外部类成员,就可以不必使用WeakReference。

上图的代码声明了一个匿名内部类的Handler,持有当前activity的引用,延时两分钟发送一个消息。如果两分钟内销毁当前activity,Handler仍持有当前activity的引用,会导致activity无法被回收,继续占用内存。

解决方案:静态内部类+弱引用

使用WeakReference弱引用持有Activity实例。原因:弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存

示例代码:

public class TestActivity extends AppCompatActivity {
    private TextView textView;
//    private Handler handler = new Handler(){
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//            switch (msg.what){
//                case 1:
//                    textView.setText("我来自两分钟前就发出的消息");
//                    break;
//            }
//        }
//    };
    private MyHandler handler;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        textView= (TextView) findViewById(R.id.text_view);
        handler = new MyHandler(this);

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 1;
                handler.sendMessageDelayed(message,1000*60*2);//2分钟后发出消息
            }
        }).start();
    }
    //自定义Handler子类,
    private static class MyHandler extends Handler{
        //定义弱引用
        WeakReference<Activity> weakReference;
        TestActivity activity;
        //在构造方法中传入需要持有的activity实例
        public MyHandler(Activity activity){
            //使用弱引用持有activity实例
            weakReference = new WeakReference<Activity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //通过弱引用获取activity实例
            activity = (TestActivity) weakReference.get();
            switch (msg.what){
                case 1:
                    if(activity != null) {
                        activity.textView.setText("我来自两分钟前就发出的消息");
                    }
                    break;
            }
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //外部类activity生命周期结束时,同时清空消息队列、结束Handler生命周期
        handler.removeCallbacksAndMessages(null);
    }
}

 

参考文章:

Android 内存泄露:详解 Handler 内存泄露的原因与解决方案

handler导致内存泄露的真正原因

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值