Fast Understanding and Resolution of Memory Leakage in Android Non-Static Handler

Keywords: Android Java

Fast Understanding and Resolution of Memory Leakage in Android Non-Static Handler

The purpose of this article is to enable readers to quickly understand and use the provided solutions

Reasons for possible memory leaks from non-static handlers

Introduction to Looper, MessageQueue, Message

  1. When an Android application starts, an instance of Looper (including MessageQueue) is automatically created for use by the main thread of the application.
  2. Looper's main job is to work with a message object in a message queue MessageQueue.
  3. In Android, all events of the Android framework, such as Activity's life cycle method calls and button clicks, are placed in the Message message Message and then joined to the Message queue MessageQueue that Looper will process, one by one.
  4. Looper (including MessageQueue in Looper) in the main thread has the same lifecycle as the current application.

Persistent Reference Analysis for Handler

Message sending and execution process

  1. Handler is initialized on the main thread
  2. Handler Sends Message
  3. Message Enter MessageQueue
  4. Looper takes Message from Handler
  5. Message is executed using handleMessage method in Handler

Handler, Message, MessageQueue, Looper Reference Relationships

Correspond to the above Message sending and execution process

  1. Handler is initialized on the main thread: Handler holds the Looper in the main thread and gets the Looper corresponding MessageQueue
  2. Handler sends Message: target member variable of Message holds Handler (setTarget method regardless of Message)
  3. Message enters MessageQueue: MessageQueue holds Message
  4. Looper takes Message from Handler: MessageQueue releases Message
  5. Messages are executed using the handleMessage method in the Handler: Messages are no longer referenced when execution is complete, and Handlers held by the same Message are no longer referenced

Handler Persistent Reference Conclusion

From the above analysis, we can see that:
When the Handler sends a message, the Handler is always strongly referenced until the Message is processed

Static/non-static Handler differences and causes of memory leaks

[Static Handler]: When creating an instance, you do not have an instance (Activity, View, Dialog, etc.) of the object in which it was created
[Non-static Handler]: When creating an instance, hold the instance of the object in which it was created (Activity, View, Dialog, etc.)
Therefore, instance objects (Activity, View, Dialog, and so on) that create a Handler are always referenced before the Message sent by a non-static Handler is executed.
Especially when the Messagesage sent by the Handler is a delay Message, the instance object created by the Handler may be referenced invalid for a long time, causing a memory leak.
Invalid Reference: When the life cycle of an instance object is completed, such as onDestroy() of Activity, it is meaningless that the previously sent Message is executed, and the reference to the instance object is invalid.

Solution: WeakReference solves possible memory leaks

Official Solutions

The following code is from Android source, api-24, android.app.Dialog

private static final class ListenersHandler extends Handler {
    private final WeakReference<DialogInterface> mDialog;

    public ListenersHandler(Dialog dialog) {
        mDialog = new WeakReference<>(dialog);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case DISMISS:
                ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
                break;
            case CANCEL:
                ((OnCancelListener) msg.obj).onCancel(mDialog.get());
                break;
            case SHOW:
                ((OnShowListener) msg.obj).onShow(mDialog.get());
                break;
        }
    }
}

Extended solution: (The following code can be directly copied for use)

[Step 1]: Create a public Handler

package com.love;

import android.os.Handler;
import android.os.Message;

import java.lang.ref.WeakReference;

/**
 * Handler,Prevent memory leaks
 * How to use it: Declare that static internal classes inherit this class
 * Date: 2018/5/26
 *
 * @author lichuang
 */
public abstract class StaticHandler<T> extends Handler {
    private WeakReference<T> mTargets;

    public StaticHandler(T target) {
        mTargets = new WeakReference<>(target);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        T target = mTargets.get();
        if (target != null) {
            handle(target, msg);
        }
    }

    public abstract void handle(T target, Message msg);
}

[Step 2]: Create a static internal Handler to inherit the public Handler and use it (example)

package com.love;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;

/**
 * Demonstrating static Handler use
 * <p>
 * Date: 2018/5/26
 *
 * @author lichuang
 */
public class TestActivity extends Activity {
    static final int WHAT_TOAST = 1;

    public static class MyHandler extends StaticHandler<TestActivity> {

        public MyHandler(TestActivity target) {
            super(target);
        }

        @Override
        public void handle(TestActivity target, Message msg) {
            switch (msg.what) {
                case WHAT_TOAST:
                    target.showToast();
                    break;
            }
        }
    }

    private Handler mHandler = new MyHandler(this);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler.sendEmptyMessageDelayed(WHAT_TOAST, 1000);
    }

    public void showToast() {
        //do what you want
    }
}

Posted by jamesjohnson88 on Fri, 10 May 2019 22:33:29 -0700