The basic use of Handler, the source code analysis of common problems and the source code explanation of operation mechanism

1, Definition

  • A set of Android messaging mechanism / asynchronous communication mechanism.

2, Function

  • In the multi-threaded scenario, the sub thread needs to transfer the update UI operation information to the main thread to realize the processing of asynchronous messages.

3, Use

There are two ways to use:

  • One is to realize asynchronous communication through sendMessage().
  • One is to realize asynchronous communication through mHandler.post().

3.1 sendMessage() method

1. Create a Handler object. The following three methods are listed.
  • Anonymous Inner Class
// Create a Handler class object from an anonymous inner class in the main thread.
private Handler mHandler = new Handler(){
// Update the UI by copying the handlerMessage method
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        // ... perform update UI operation
        }
};
  • Implement Callback interface
// In the main thread, a handler class object is created by implementing the Callback interface.
private Handler mHandler  = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
});
  • Create a Handler class object in the child thread (relatively rare, basically not used)
 new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler mHandler = new Handler();
                Looper.loop();
            }
        }).start();
2. Create a Message object and send a Message to the handler at the same time.
  1. Create Message object
// Mode 1:
Message message1 = new Message();
// Mode 2:
Message message2 = Message.obtain();
// Mode 3:
Message message3 = mHandler.obtainMessage();
  1. The Message object carries data
  Message message = new Message();
  message.what = 1; // Identification of the message

  // Method 1: pass through arg1 and arg2
  message.obj = "La La La"; // object
  message.arg1 = 1; // int type
  message.arg2 = 2; // int type

  // Method 2: transfer through Bundle
  Bundle bundle = new Bundle();
  bundle.putInt("1",1);
  message.setData(bundle);
  1. handler sends a message
// Method 1: send ordinary message
mHandler.sendMessage(message);

// Method 2: send an empty message and pass in the what ID value to facilitate judgment in the received message. 
mHandler.sendEmptyMessage(0);

// Mode 3: delay sending message
mHandler.sendEmptyMessageDelayed(0,1000);
3. The handler receives the message and processes it.
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        // Judge and process different messages through the what id when sending messages
        switch (msg.what) {
            case 0:
                System.out.println("The received message is empty");
                break;
            case 1:
                System.out.println(msg.obj.toString());
                break;
            default:
                break;
            }
       }
};

3.2 mHandler.post() method

  • 1. Create a Handler object.
  • 2. In the child thread, pass in the runnable object by calling the post() method of the instance object, rewrite the run() method, and update the UI in the rewritten run() method.
private Handler mHandler = new Handler();
new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                       // Update UI action
                    }
                });
            }
        });

Well, that's all about the basic use of Handler. Let's deepen our understanding of Handler by explaining the problems one by one and the source code of the corresponding problems.

4, FAQs and source code answers

1. What is the difference between creating a Handler by the main thread and creating a Handler by the child thread?

Main thread create Handler:

private Handler mHandler  = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
});

Create Handler for child thread:

 new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler mHandler = new Handler();
                Looper.loop();
            }
        }).start();

Through comparison, we can see that there are two more lines of code to create a Handler in the sub thread, so why not when the main thread is created? Let's explain through the source code:

When you start, you call the ActivityThread class. In the main() method of this class, you call Looper.prepareMainLooper():

// main method in ActivityThread class
 public static void main(String[] args) {
        // ....
        // Create Looper for main thread
        Looper.prepareMainLooper();
        //...
        Looper.loop();
    }

Looper.prepareMainLooper();

  • The meaning of this line of code is to initialize a Looper for the current thread, so it is not that the main thread does not need to call the Looper.prepare() method, but that the system has already done the operation.

Looper.loop();

  • The meaning of this line of code is to start the polling of the main thread. This is an endless loop and will poll all the time.

After reading this, someone may want to ask: when the main thread is created, the system automatically adds these two lines of code for us. If we don't add these two lines of code when the sub thread creates the Handler, will you? The answer is no, no, exceptions will be reported:

 throw new RuntimeException("Only one Looper may be created per thread");

At this time, we will think, why does the system throw exceptions? We still need to find the answer from the source code. At this time, we need to see what is done in the source code when new Handler()?

 public Handler(@Nullable Callback callback, boolean async) {

       //.....
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
       //....
    }

From the source code, we can see that the member variable mLooper will be assigned a value. Once the value is not obtained, the exception mentioned earlier will be thrown, so we need to see what the operation Looper.myLooper() does?

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

 //......
 public static @Nullable Looper myLooper() {
      return sThreadLocal.get();
  }

From the source code, we can see that the return value of this method is the Looper object. Then let's take a look at the get() method. How to get this Looper object?

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

From this view, we can see that the object of the current thread is obtained as the key value, and then the value value is obtained from the ThreadLocalMap. From the result, it is not obtained. Why?, Let's go back to the main() method in the ActivityThread class and click the prepareMainLooper() method of Looper.prepareMainLooper():

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

Then click the prepare() method in this method:

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

The following is the sThreadLocal.set() method:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

When we look at the two methods, we can see that when the application starts, the only data will be created in the ThreadLocalMap. The value of the key is the main thread, and the value is the Looper object of the main thread. When we create a Handler in the sub thread, we need to obtain the Looper object, so the value of the passed in key is the sub thread, Of course, the Looper object cannot be obtained from ThreadLocalMap, because the value of the key is different. Therefore, if the Looper object is null, the above exception is thrown.

Through this question, we can make the following summary:

  • The child thread does not have a Looper by default. If you need to use a Handler, you must create a Looper for the thread.
  • A thread must have a Looper, that is, if you want to use a handler in a thread, you must have a Looper. To correctly create a handler object in the child thread, do the third method of creating a handler object above.
  • When the handler is created, the Looper of the current thread will be used to construct the message loop system. Because the ActivityThread class will initialize the Looper when the application is started, the handler can be used by default in the main thread, but it cannot be said that the main thread does not create the Looper object.

2. Can updating the control content really only run in the main thread?

First of all, give a conclusion. This sentence is too absolute and not necessarily. Children's shoes that don't believe can update the UI in the child thread. The result may surprise you and doubt yourself. Ha ha, let's take TextView.setText() as an example to analyze a wave from the perspective of source code:

Click the setText() method all the way to the end and find the checkforrelay () method in the last setText method. Click this method to directly see the last code. You can see that both the code in if and the code in else will eventually go through two lines: requestLayout() (request layout) and invalidate() (refresh layout). Then let's look at what's done in requestLayout()?

public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();

        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy

            //  ------ViewRootImpl implements the mParent interface--------
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }

        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;

        if (mParent != null && !mParent.isLayoutRequested()) {
            //  ---------mParent is an interface---------
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

ViewRootImpl exactly implements the mParent interface, that is, it will call the requestLayout() method in ViewRootImpl. Let's take a look at the source code:

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

There is a method to check threads, checkThread(). Click to enter:

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

This code will check whether the current thread is the main thread, and throw an exception if it is not.

It can be seen from this that we update the UI in the child thread. If the execution speed of the requestLayout() inspection thread is slower than that of the invalidate() drawing interface, no exception will be thrown.

We can do an operation to verify. Delay one second in the sub thread to update the UI. The error message is as follows:

 Process: com.example.handlerdemo, PID: 10569
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:360)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.TextView.checkForRelayout(TextView.java:8908)
        at android.widget.TextView.setText(TextView.java:5730)
        at android.widget.TextView.setText(TextView.java:5571)
        at android.widget.TextView.setText(TextView.java:5528)
        at com.example.handlerdemo.MainActivity$3.run(MainActivity.java:75)
        at java.lang.Thread.run(Thread.java:764)

It can be seen that the error reporting place pointed to is the place where we can see the inspection thread ViewRootImpl.requestLayout after reading the source code.

That's why it's said that the UI update operation can only be performed on the main thread. This sentence is too absolute. It doesn't have to be. It depends on the situation analysis.

3. What is the difference between the two methods of creating Handler by main thread?

Mode 1:

 private Handler mHandler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
    });

Mode 2:

    private Handler mHandler2 = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            mTextView.setText(msg.obj.toString());
        }
    };

Everyone who has used it knows that there is a yellow warning in mode 2. This is the api of Google spare tire and is not recommended.

Based on the previous analysis, we all know that in the main() method of actitivitythread class, in addition to creating a Looper object, we will also call the Looper.loop() method to continuously cycle the message queue. Click this method:

// Looper class

 for (;;) {
 //....
 try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } 
 }
 //...

In this loop, the dispatchMessage() method of msg.target will be called to distribute messages. What is this msg.target? MSG is the Message object. We enter the source code of the Message class and see the following code:

// Message class

@UnsupportedAppUsage
/*package*/ Handler target;

It can be seen that msg.target is the Handler object, so it actually calls the dispatchMessage() method of the Handler. Let's take a look at the dispatchMessage() method in the Handler class:

// Handler class

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

The judgment in if is prepared for the mHandler.post() method. We don't look at it first, but directly see the method in else. First, we judge whether mCallback is empty. mCallback is a member variable in the Handler class, which is an interface:

// Handler class

final Callback mCallback;
public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

The way we write in method 1 is to pass in a callBack object of a new Handler, so if it is judged not to be empty, we will go to mCallback.handleMessage(); Method, that is, we implement the handlemessage () method in the callBack interface. In our rewritten method, there is no difference between return true or return false. Why?

If we return true, we will return directly in the source code. If we return false, we will follow the following handleMessage() in the source code. This is an open method exposed in the Handler class that can be rewritten, but its method name is the same as that in the interface. We can't rewrite it. In the Handler class of mode 1, we can't rewrite the handleMessage() method, That's why it makes no difference to write return true or return false.

If we use mode 2, we will follow the handleMessage() method below. That is, go to the rewritable public method handleMessage() exposed in the Handler class we overridden in mode 2.

Well, that's the difference between the two methods. Now let's talk about why the code in if in the dispatchMessage() method to be ignored is prepared for mHandler.post()?

Let's take a look at our mHandler.post():

new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler1.post(new Runnable() {
                    @Override
                    public void run() {
                          //.....
                    }
                });
            }
        });

Let's take another look at the post() source code in the Handler class:

// Handler class

public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

You can see the Runnable object we passed in. We will call the getPostMessage(r) method to encapsulate the Runnable object we passed in. So what does this method do?

// Handler class

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

You can see that the incoming Runnable object is assigned to Message.callback in the source code, and this Message.callback is the callback attribute in the Message class, which is a Runnable object:

 // In the Message class

 @UnsupportedAppUsage
 /*package*/ Runnable callback;

The value of the callback attribute of the Message object returned by the above method is the Runnable object passed in by the post() method.

Let's look back at the code in if in the dispatchMessage() method of the Handler class:

// Handler class

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

For the message sent by mHandler.post(), the code in if will be executed when receiving the message. Because msg.callback is the incoming Runnable object and is not empty, the handleCallback() method will be executed:

// Handler class

private static void handleCallback(Message message) {
        message.callback.run();
    }

You can see that the handleCallback() method executes the run() method of message.callback, that is, the run method rewritten by the Runnable interface we passed in.

4. What is the difference between the two methods of creating Message?

Mode 1:

Message message = Message.obtain();

Mode 2:

Message message = mHandler.obtainMessage();

Let's first look at the source code of the obtain() method in the Message class in method 1:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

From the source code, Android will provide a cache pool for messages to cache used messages for next use. When we use the Message.obtain() method to obtain a Message, we will first obtain it from the cache pool. If there is no Message in the cache pool, we will create a Message. This optimizes memory. At the same time, the cache pool sPool will point to the next of the Message.

Now look at the source code of obtainMessage() in mode 2:

 public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

It can be seen from the source code that the Messageobtain() method is actually called and the current handler object is passed as a parameter. Let's take a look at the source code in the obtain() method:

 public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

It can be seen from the source code that the obtain() method called in the first line is the same as the method called Message.obtain() in the source code of the first method, except for the second line of code, which assigns the handler to message.target. With this operation, instead of writing the handler.sendMessage() method, you can directly write the message.sendToTarget() method. Why? Let's take a look at the source code of the sendToTarget() method:

public void sendToTarget() {
        target.sendMessage(this);
    }

From the source code, target is the previously assigned handler. this refers to the message currently called, which is equivalent to handler.sendMessage(message).

5. Why does improper use of handler cause memory leakage?

Remember that when we use the second method to create a Handler, there will be a yellow warning to the effect that the Handler is not set to static, and the final memory leak will occur on the external class holding the Handler class, such as MainActivity.

Then we have to think about why the Handler created in the second method is not set to static, so memory leakage may occur??

First of all, we need to know two points:

  • When the Handler is created, it will get the Looper object of the main thread, and the lifecycle of this Looper object is the same as that of the application.
  • Basics of Java: non static internal classes or anonymous internal classes hold references to external classes by default.

Based on the above two theoretical knowledge, we imagine a scenario. If there are still unprocessed messages waiting to be processed in the message queue obtained from the main thread in the Handler, the message in the message queue holds the reference of the Handler by default, and the Handler holds the reference of the external class such as MainActivity by default, Assuming that the Handler is created in the MainActivity, if we destroy the MainActivity, the garbage collection period GC will fail to recycle the MainActivity due to the above reference relationship, resulting in memory leakage.

Now that we know the cause of memory leak, how to solve it? Of course, it starts from the reason:

  • Static inner class + weak reference.
  • When the external class is about to be destroyed, empty the message queue of the Handler and set the Handler to null.

The principle of the first method is that static internal classes do not hold references to external classes by default, and weakly referenced objects have a short life cycle. When the garbage collector thread scans, once an object with only weak references is found, its memory will be reclaimed regardless of whether the current memory space is sufficient or not.

The principle of the second method is to clear the message queue so that the reference relationship does not exist, and synchronize the life cycles of MainActivity and Looper.

The first approach:

public class HandlerActivity extends AppCompatActivity {

    private final MyHandler mHandler = new MyHandler(this);

    private static final Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            // operation
        }
    };

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

        mHandler.postDelayed(mRunnable, 1000*10);

        finish();   
    }

    private static class MyHandler extends Handler {
        WeakReference<HandlerActivity> mWeakActivity;

        public MyHandler(HandlerActivity activity) {
            this.mWeakActivity = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            final HandlerActivity mActivity = mWeakActivity.get();
            if (mActivity != null) {
                // Processing messages
            }
        }
    }

}

The second approach:

 @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("TAG  >>>>","onDestroy");

        // Properly handle memory leaks
        mHandler.removeCallbacksAndMessages(null);
        mHandler = null;
    }

5, Explanation of Handler mechanism source code

With the explanation of these knowledge points, let's grasp the operation mechanism of the Handler as a whole by reading the source code and see how the Handler sends and processes messages.

Let's start with a picture worth tens of millions:

For Hanlder's asynchronous message mechanism, we are divided into four steps:

Step 1:

When the application starts, the main() method in the ActivityThread class will call the Looper.prepareMainLooper() method, and the prepareMainLooper() method will be called in prepareMainLooper(). In this method, a globally unique main thread Looper object will be created. Why do you say so? Take a look at the source code of the prepare() method:

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

Insert the new Looper into the ThreadLocalMap through the sThreadLocal.set() method. The value of key is the current thread, that is, the main thread. The value of value is the Looper of the main thread. Let's take a look at the constructor of Looper, which is private:

// Private
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Therefore, the Looper object can only be created through the static method prepare() exposed by the Looper class. Therefore, the Looper object of the globally unique main thread is created. At the same time, the MessageQueue object is also created in the Looper constructor, which is also the message queue of the globally unique main thread.

Step 2: when we create a Handler object in the main thread, that is, new Handler, check the source code:

public Handler(@Nullable Callback callback, boolean async) {
      //..
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread " + Thread.currentThread()
                       + " that has not called Looper.prepare()");
       }
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
   }

You can see that the member variables mloop and mQueue in the Handler are assigned initial values. The Looper object created by the main thread is assigned to the member variable mloop in the Handler class, and the message queue in the Looper object in the main thread is assigned to the member variable mQueue in the Handler class.

Step 3:

When the Handler sends a message, we know that there are many ways for the Handler to send a message:

However, no matter how many methods there are to send messages, the enqueueMessage() method will eventually be called by viewing the source code:

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

One thing worth paying attention to in the above source code is msg.target = this; This line of code, this is the Handler object calling this method, assigns the Handler object to the target attribute of the incoming Meessage object.

We see that this method calls the queue.enqueueMessage method again. We find the enqueueMessage method in the MessageQueue class:

boolean enqueueMessage(Message msg, long when) {
       //...

        synchronized (this) {
            //...
            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 {
                //...
            }
            //....
        }
        return true;
    }

We see a line of code, mMessages = msg, This mMessages is the member variable Message in the MessageQueue Message queue class, so we understand that when the Handler sends a Message, it actually passes the Message (msg) to be sent to the Message queue (MessageQueue class) of the main thread, and assigns the value of msg to the mMessages property in the Message queue (MessageQueue class) of the main thread.

Step 4:

How does the Handler receive and process messages? In fact, after our application is started, loop. Loop() will be called in the ActivityThread to read the message circularly. Check the source code of loop() method:

 public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
         //....
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            //...
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
             //.....
        }
    }

We see a line of code msg.target.dispatchMessage(msg);, This is the Looper message pump, which always fetches messages from the message queue. Once a message is fetched from the message queue, it will call the dispatchMessage () method to distribute the message. So what is this msg.target? Remember that no matter which message sending method the Handler calls, it will eventually call the enqueueMessage() method? In that method, the caller Handler is assigned to the Message.target attribute. Therefore, the method of distributing messages here is to call the dispatchMessage () method of the Handler itself. For the dispatchMessage() method of the Handler itself, we have analyzed it before:

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

If you send a message by calling the mHandler.post() method, you will follow the code in the if above when processing the message, and execute the re Abstract run() method of the Runnable interface implementation class passed in by mHandler.post(Runnable r).

If you create a Handler object by implementing the callback interface, go to the code in if in else and call the handleMessage() method that implements the callback interface.

If you create a Handler object by anonymous object class and overriding the handleMessage() method exposed in the Handler class, then go to the last handleMessage() method.

To learn more about the underlying Android knowledge points, such as Handler, Binder and other related knowledge points, you can click the small card below to access and consult


Tags: Java Android Framework source code Handler

Posted on Sat, 16 Oct 2021 13:36:22 -0400 by dean7