欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 常见的Handler内存泄露

程序员文章站 2022-07-14 18:29:42
...

1.问题发现:

在平时使用Handler的时候,我们通常会这样定义:

// 定义一个Handler对象,并实现handleMessage方法
Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 在此接收子线程发送的消息
    }
};

嗯,看起来没有什么问题,但是Android Lint却给出了警告:

This Handler class should be static or leaks might occur

意思是说这个Handler应该定义成静态的,否则可能会内存溢出。什么鬼,怎么还会内存溢出,于是百...额...不对...Google一下,发现Google的工程师Romain Guy已经在论坛中做出过解释:

I wrote that debugging code because of a couple of memory leaks I found in the Android codebase. Like you said, a Message has a reference to the Handler which, when it's inner and non-static, has a reference to the outer this (an Activity for instance.) If the Message lives in the queue for a long time, which happens fairly easily when posting a delayed message for instance, you keep a reference to the Activity and "leak" all the views and resources. It gets even worse when you obtain a Message and don't post it right away but keep it somewhere (for instance in a static structure) for later use.

学好一门外语是多么的重要,翻译翻译:

我在Android代码库中发现了一些内存泄漏,为此我写了调试代码进行测试,像你说的一样,Message会持有一个对Handler的引用,当这个Handler是非静态内部类的时候,又会持有一个对外部类的引用(比如Activity)。如果发送一条延时的Message,由于这个Message会长期存在于队列中,就会导致Handler长期持有对Activity的引用,从而引起视图和资源泄漏。当你发送一条延时的Mesaage,并且把这个Message保存在某些地方(例如静态结构中)备用,内存泄漏会变得更加严重。

先说下什么是内存泄漏:

内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

写段代码说明一下:

public class HandlerActivity extends BaseActivity {

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

        handler.sendEmptyMessageDelayed(0, 10 * 60 * 1000);
        finish();
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // ...
        }
    };
}
使用Handler发送一条延时消息,然后关闭Activity,在Activity被关闭之后,Message将在消息队列中存在10分钟才能被执行到,这个Message持有一个对Handler的引用,Handler又持有一个对当前Activity的引用,这些引用会在Message被执行之前一直保持,这样当前Activity就不会被垃圾回收机制回收,从而导致内存泄漏。

2.如何解决

那么,该如何解决这个问题呢,Romain Guy给出了他的建议写法:

class OuterClass {
                                 
  class InnerClass {
  
    private final WeakReference<OuterClass> mTarget;
                                 
    InnerClass(OuterClass target) {
           mTarget = new WeakReference<OuterClass>(target);
    }
                                 
    void doSomething() {
           OuterClass target = mTarget.get();
           if (target != null) {
                target.do();    
           }
     }
}
在内部类的构造方法中,创建一个对外部类的弱引用,然后再内部类的方法中通过弱引用获取外部类对象,进行非空判断后再进行操作,OK,修改一下我们的代码:
public class HandlerActivity extends BaseActivity {

    @Bind(R.id.tv_handler)
    TextView tvHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        ButterKnife.bind(this);

        new WeakHandler(this).sendEmptyMessageDelayed(0, 10 * 60 * 1000);
        finish();
    }

    private static class WeakHandler extends Handler {

        WeakReference<HandlerActivity> weakReference;

        public WeakHandler(HandlerActivity activity) {
            weakReference = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            HandlerActivity activity = weakReference.get();
            if (activity != null && activity.tvHandler != null) {
                activity.tvHandler.setText("收到Handler发送的消息");
            }
        }
    }
}
因为静态内部类不会持有对外部类的引用,所以定义一个静态的Handler,这样Acitivity就不会被泄漏了,同时让Handler持有一个对Activity的弱引用,这样就可以happy的在Handler中调用Activity中的资源或者方法了。