The foreword 0.

Handler is a common part of Android code and interviews, so it’s worth taking some time to sort it out.

1. What is Handler?

1.1 define

The source code out of the content, English is not difficult to understand. Basically, each Handler is bound to each thread and its corresponding message queue. Messages can then be passed between threads via handlers.

A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread'Message queue. When you create a new Handler, it is bound to the thread, message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue. <p>There are two main usesfor a Handler:
  (1) to schedule messages and runnables to be executed at some point in the future; and 
  (2) to enqueue an action to be performed on a different thread than your own.
Copy the code

1.2 Functions of Handler

Handler has two functions:

  1. Tell the program to do something at some point in the future
  2. Events pass execution across threads (the main use is for child threads to let the main thread do some refreshing)

1.3 Basic Usage of Handler

1. Execute events in Runnable at some future time

handler.post(runnable,delayMillis);
Copy the code

2. Events are transmitted between different messages

android.os.Handler handler = new Handler(){@override public void handleMessage(final Message MSG){// accept and process the Message}}; // sendMessage handler.sendMessage(message);Copy the code

2. Basic principles of Handler

2.1 Classes related to the Handler principle

  • Handler
  • Looper
  • MessageQueue
  • Messagre

2.2 Overview of Handler Principles

Let’s start with a schematic of Handler message processing

2.3 Handler Workflow

2.3.1 create Handler

The first step is to create the Handler, which has the following constructors

The parameters are as follows

  • boolean async

    Async This parameter determines whether messages sent by the Handler should be set to asynchronous. If true is set to asynchronous, asynchronous messages can be used in conjunction with synchronous blocking, a typical example being timed refresh of an interface.

  • Callback callback

    If this parameter is set, the code in the dispatchMessage callback will be called first by the Handler during the dispatchMessage callback. If true, the handleMessage callback will not be executed

/**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if(msg.callback ! = null) { handleCallback(msg); }else {
            if(mCallback ! = null) {if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}Copy the code
  • Looper looper

    Set the Looper parameter corresponding to the Hanlder. For each Handler, there is a corresponding Looper, and each Looper has its corresponding MessageQueue. If the Handler’s Looper is not explicitly set, the Handler defaults to assigning the thread’s Looper to the Handler.

2.3.2 The Handler sends messages

The Handler mainly calls the sendMessage method when sending messages.

sendMessage(@NonNull Message msg)
Copy the code

Methods that send messages have many different names and arguments. However, if you look at the source code step by step, you will end up with the following code

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
Copy the code

If we look at this code, we can see that the main operation is to call the queue method. The parameters passed in are the queue, message, and corresponding event

2.3.3 MessageQueue Presses messages

This method firstly sets various parameters of Message instance, the most important of which is the target parameter, which is used to determine which Handler’s dispatchMessage will be called after the Message is removed from MessageQueue, so as to process the Message.

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
Copy the code

When the parameters are set, the enqueueMessage of the Handler calls the enqueueMessage method of MessageQueue

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr ! = 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }Copy the code

From the code, we can see that message queues are mainly stored in the form of linked lists. All this code does is find the point to insert from the list based on the when parameter in Message and enter Message.

2.3.4 Looper Retrieves information

We call the loop method of the Looper class, which contains a for(;). Loop to retrieve messages from MessageQueue.

 for(;;) { Message msg = queue.next(); ... }Copy the code
2.3.5 Handler Processes messages

When the message is retrieved, it is called

 msg.target.dispatchMessage(msg);
 
Copy the code

To handle messages, and this target immediately started the Handler that enqueMessage was stuffed into.

As we can see from Handler, this dispatchMessage(Message MSG) method.

 public void dispatchMessage(@NonNull Message msg) {
        if(msg.callback ! = null) { handleCallback(msg); }else {
            if(mCallback ! = null) {if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}Copy the code

HandleMessage (MSG) is the callback to our custom Handler so that the message goes through the process.

3. The extension Handler

3.1 Why Q handlers cause memory leaks and how can I handle the memory leaks?

A: The common uses of Handler are as follows:

  Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
           
        }
    };
Copy the code

Because handlers are implemented as anonymous inner classes, they have references to external classes (usually activities), which can cause memory leaks.

The usual solution is as follows

A weak reference can be used if a reference to an external class parameter is required

Private Static Class MyHandler extends Handler{// Holds a weak reference to a HandlerActivity that is reclaimed by the GC WeakReference<HandlerActivity> mActivty; public MyHandler(HandlerActivity activity){ mActivty =new WeakReference<HandlerActivity>(activity); } @Override public void handleMessage(Message msg) { HandlerActivity activity=mActivty.get(); super.handleMessage(msg);if(activity! =null){// Execute business logic}}}Copy the code

3.2Q Handler class has an interface for Callback.

A: This is designed to address Handler memory leaks.

Refer to the following code:

Handler handler = new Handler(new Handler.Callback(

@Override

public boolean handleMessage(Message msg) {

if (msg.what ==1){

textView.setText("Hello!");

return true;

    }

return false;

));

Copy the code

Handler hadlerMessage (Message MSG) if the mCallback variable is undefined and empty, the Handler system will call hadlerMessage (Message MSG) if the mCallback variable is defined. Then it will run to mCallback

 public void dispatchMessage(@NonNull Message msg) {
        if(msg.callback ! = null) { handleCallback(msg); }else {
            if(mCallback ! = null) {if (mCallback.handleMessage(msg)) {
                    return; } } handleMessage(msg); }}Copy the code

3.3 What is the relationship between ordinary messages, asynchronous messages and message barriers in Q Handlers? What does it do?

There are three types of messages in MessageQueue: ordinary messages, asynchronous messages, and Message barriers. The difference is that the message barrier target is NULL. Ordinary and asynchronous messages are distinguished by setAsynchronous being true.

After message barriers are set up, asynchronous messages have priority in processing. This mechanism is used for periodic refresh of the interface

3.4 When was the target of Q Message injected?

Handler (); Handler ()

 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Copy the code

The Handler that stores the Message to the Message queue is called back to the Handler’s dispatchMessage method.

3.5 Will Q Looper constantly fetch messages from message queues?

No. If MessageQueue has run out of messages or the message will be triggered at a later event, the native method nativePollOnce(long PTR, int timeoutMillis) will be called to make the main thread wait. The nativeWake(Long PTR) method is called to wake up the main thread when a message is pushed to the message queue, so although there is a for loop in the loop method, this does not cause Looper to keep fetching data from the message queue. See the nativePollOnce article on MessageQueue on Android

3.6 Q How does the post(Runnable R) method of the Q Handler implement the execution of events in Runnable?

The POST method is essentially a message-passing process.

 public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
Copy the code
 private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
Copy the code

As you can see from both ends of the code, it encapsulates runnable and inserts runnable into the Message. When the Handler gets the message, it fetches the msG. callback argument and runs the runnable method inside.

3.7Q How to get Message objects in normal code writing?

It is better to use message.obtain () to avoid the performance cost of GC.

3.8Q What is IdleHandler and what does it do?

IdleHandler is an object that is fetched and executed when the main thread message queue is idle. Can be used in some page optimization scenarios. You can refer to my previous article IdleHandler, a page-launching optimization artifact

4. Reference materials

  1. Android Handler stuff, message barriers? IdelHandler? ANR?
  2. Android MessageQueue nativePollOnce
  3. IdleHandler, page start optimization artifact
  4. Understand the triggering principle of Android ANR