Android About Handler Memory Leakage

Keywords: Android Google ButterKnife

In the last article Full Analysis of Android Handler Mechanism In this article, we analyze the Hanlder mechanism from the point of view of source code, and then continue to learn about Handler. This article focuses on the memory leaks that Handler may cause and solutions.

1. Why does memory leak occur?

When we use Handler in our daily life, we usually define it as follows:

// Define a Handler object and implement the handleMessage method
Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // Receive messages sent by sub-threads here
    }
};

Well, it doesn't seem to be a problem, but Android Lint gives a warning:

This Handler class should be static or leaks might occur

This means that the Handler should be defined as static, otherwise memory overflow may occur. What the hell, how can memory overflow, so hundred.. Forehead... No. Google found that Google Engineer Romain Guy had already explained in the forum:

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.

How important it is to learn a foreign language well.

I found some memory leaks in the Android code base, so I wrote debugging code to test. As you said, Message holds a reference to Handler, and when the Handler is a non-static internal class, it holds a reference to an external class (such as Activity). If a delayed message is sent, because the message will persist in the queue for a long time, it will cause Handler to hold a reference to Activity for a long time, thus causing view and resource leakage. Memory leaks become more serious when you send a delayed Mesaage and save the message somewhere (for example, in a static structure) for backup.

Let's start with what memory leaks are:

Memory Leak (MLK) refers to the serious consequences of the program running slowly or even the system crashing because the dynamically allocated heap memory in the program is not released or can not be released due to some reason, resulting in the waste of system memory.

Write a code to illustrate:

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);
            // ...
        }
    };
}

Send a delay message using Handler, then close Activity. After Activity is closed, Message will exist in the message queue for 10 minutes to be executed. This Message holds a reference to Handler and Handler holds a reference to the current Activity. These references will remain until Message is executed, so that the current Activity will not be garbage returned. Receiving mechanism reclaims, resulting in memory leaks.

2. How to Solve the Problem

So, how to solve this problem, Romain Guy gives his suggestion:

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();    
           }
     }
}

In the construction method of the inner class, create a weak reference to the outer class, then get the outer class object through the weak reference in the inner class method, make non-null judgment and then operate, OK, modify our code:

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("Received Handler Messages sent");
            }
        }
    }
}

Because static internal classes do not hold references to external classes, a static Andler is defined so that Acitivity is not leaked and Handler holds a weak reference to Activity so that happy can call resources or methods in Activity in Handler.

If you do not need to process messages in the message queue after closing Activity, you can add the following code in the onDestory method:

// Remove all messages
handler.removeCallbacksAndMessages(null);

// Remove a single message
handler.removeMessages(what);

Well, the memory leaks that Handler can cause and the solutions are all over here.

3. Write at the end

Students are welcome to comment on Tucao. If you think this blog is helpful to you, leave a message or go ahead.

Posted by Mijii on Fri, 21 Jun 2019 17:22:43 -0700