When you think of asynchrony, the first thing that comes to mind is multithreading, and concurrency in multithreading is when the CPU schedules the computation so that it looks like it’s happening at the same time to the user, but it’s not actually happening at the same time from the CPU level. The CPU schedules computations so that only one thread is executing at the same time. However, the scheduling computations are very fast, and the threads are selected randomly, so it looks like we are executing at the same time. Here is an example:

private static class ThreadOne extends Thread {

    private static final String ThreadName = "ThreadOne";

    @Override
    public void run() {
        super.run();

        for (int i = 0; i < 100; i++) {
            Log.i(TAG, "run: " + ThreadName + "Carried out the mission."+ i); }}}Copy the code
private static class ThreadTwo extends Thread {

    private static final String ThreadName = "ThreadTwo";

    @Override
    public void run() {
        super.run();

        for (int i = 0; i < 100; i++) {
            Log.i(TAG, "run: " + ThreadName + "Carried out the mission."+ i); }}}Copy the code
private void threadTest() {

    final ThreadOne threadOne = new ThreadOne();
    final ThreadTwo threadTwo = new ThreadTwo();

    threadOne.start();
    threadTwo.start();
}Copy the code

The example is simple: create two threads and call the start() method to start the threads

The above example runs as follows: (Partial result screenshot)




The results may vary from phone to phone, but we can still conclude:

In multithreaded async, multiple paths are executed without interference, so the order in which they are executed is unknown. It depends on the CPU’s scheduling calculation. Normally, you have no control over which threads are executed first, which threads are executed after, or which threads are terminated after.

When we talk about Handler, we sometimes talk about asynchronous distribution, but is “asynchronous” the same as “asynchronous” with multiple threads? Let’s look at the following small example:

private void test() {

    Log.i(TAG, "OnCreate: Before event distribution");
    sHandler.sendEmptyMessage(0);
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Log.i(TAG, "Test: After event distribution");
}Copy the code
private static Handler sHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.i(TAG, "HandleMessage: Event handling callback"); }};Copy the code

We execute the test() method in the onCreate() method, and let the main thread slee for 2 seconds after the event is distributed through hander.

After running the above program, the output is as follows:




Do you see the problem? In the case of multiple threads, the code in each thread is executed alternately, but in the Handler example above, instead of alternating, the Handler’s callback is executed after our test() method is finished. This does not appear to be an asynchronous operation. It’s a synchronous operation, except that the Handler callback code is executed last. Why? Let’s analyze:

First of all, we need to understand the operation principle of Handler. Our main thread will be equipped with a Message queue MessageQueue, and only one Message queue. Handler is the sending receiver and processing of messages.




In this way, the sending and processing of messages do not happen continuously. It is not that I immediately execute the message after sending it, but that the message is now sent to the tail of the corresponding message queue in the thread. When the previous message in the message queue is finished, it will be his turn to execute.

Furthermore, in Android, all events run on an event-driven basis, and the activity declaration cycle is no exception. In the app’s entry class ThreadActivity, we define an inner class H, which is a Handler. The declaration cycle of an activity is also distributed via a handler. If you are interested, check out the source code. This is not our focus.

Here, LET’s take a look at our Handler example:

Now let’s assume that our test() method is message A, and the message sent through the Handler in the test() method is message B. For the sake of intuition, let’s assume that message A is loaded with blood (forgive me for being poisoned by the king):



As we said earlier, the onCreate() method is also distributed through the Handler that the program uses to send our messages to the main thread’s MessageQueue MessageQueue. The test() method is executed in onCreate. Let’s just look at the test() method, which means that the state of the message A will be executed looks like this:



At this point, message A starts to execute, and when the first slot is finished, it will look like this:



At that time, blood slot 2 begin to execute, in this is through a distributed event handler, B is the test () method of sHandler. SendEmptyMessage (0);

MessageQueue now has the following state:



MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue Our message B can only be executed, which is why our example above prints the order.

From the above analysis, we can know that the so-called “asynchronous” Handler is not really asynchronous, but the sending and processing of messages are not only executed in sequence, and our program does not block until the completion of the handle callback to continue execution, but continue to execute down. When the current message execution is complete, the next message is retrieved from the message queue for execution. The final execution is not asynchronous, but synchronous, except that message B is placed after event A, and event B is executed when time A has finished.

The Handler’s final callback is also on the main thread. We can’t perform time-consuming operations in its callback methods, not just the handler.sendMessage() method. He does the same thing when we call handler.post().

This reminds me of a problem I had when I was learning how to draw a view.

We know that the view measure is drawn in the ViewRootImpl class, which is created after the Activity’s onResume() method, so we get the view width and height in the onCreat() method, and we get 0. If you want to get the width and height from the onCreate() method, one of the ways to do this is through the view.post() method.

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if(attachInfo ! = null) {return attachInfo.mHandler.post(action);
    }

    // Postpone the runnable until we know on which thread it needs to run.
    // Assume that the runnable will be successfully placed after attach.
    getRunQueue().post(action);
    return true;
}Copy the code

AttachInfo is an inner class of the View, and in the onCreate() method the View has not been created yet, so AttachInfo is null, which is why it calls the following method:

getRunQueue().post(action);
Copy the code

After this method is called, it puts the Runnable object passed in to the RunQueue. It’s just put into the RunQueue, but it’s not executed. Where is the actual execution? In the performTraversals() method of ViewRootImpl, here I simplify the traversals method for the sake of analysis:

performTraversals() {// omit..... getRunQueue().executeActions(mAttachInfo.mHandler); performMeasure(); performLayout(); performDraw(); }Copy the code

This actually executes the message in the runqueue, but the problem is that we call the runqueue first, and then the performMeasure() method, so we can’t get the width and height of the view in view.post(). But we can get it, and in fact the reason we analyzed above is that when performTraversals() is executed, it blocks at the head of the queue, Then calling getRunnQueue().executeAction() will send the run-queue message to the message queue, but only after performTraversals(). Only when the performTraversals() method is finished can the message in the run queue be executed.