Android message mechanism source code analysis

I plan to change jobs, and I have forgotten a lot of things. Therefore, this series of articles is mainly for interviews, and I will go through some basic knowledge points. Plan to spend a month preparing for the interview!

Today's topic:

Android message mechanism source code analysis

Let's talk about the classes involved in the Android message mechanism: Handler, Looper, MessageQueue, Message, ThreadLocal.

When I was thinking about this problem before, I thought about a few problems. After these problems are understood, the message mechanism is solved:

  1. Where did you post the message?
  2. Where did the message go?
  3. How is the message finally processed?
  4. How to change the message sent by the child thread to the main thread?
  5. Where did Looper come from?
  6. Where is the loop method of Looper called?
  7. What is the function of each of the above categories?
  8. Where is the Handler (main thread or child thread)?
  9. Where is the Looper (main thread or child thread)?
  10. Where is the MessageQueue (main thread or child thread)?
  11. Where is ThreadLocal (main thread or child thread)?

This article mainly covers the following contents:

  1. The application of Android message mechanism in actual development;
  2. Source code analysis of message mechanism;
  3. The answers to the above questions are revealed;

Application of Android Message Mechanism in Actual Development

Before the official start, let's take a look at a specific use case of Handler in multi-threading:

A very simple requirement: update the content of the TextView after clicking the button on the page.

public class MainActivity extends AppCompatActivity {

    private  TextView mTextMessage;
    private Button button;

    private UpdateHandler updateHandler;

    static class UpdateHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;

        public UpdateHandler(MainActivity activity) {
            mActivity = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            MainActivity instance = mActivity.get();

            if (msg.what == 1) {
                instance.mTextMessage.setText("update====");
            }
        }
    }

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

        mTextMessage = (TextView) findViewById(R.id.message);

        updateHandler = new UpdateHandler(MainActivity.this);

        button = (Button) findViewById(R.id.btn_start);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                updateTextViewContent();
            }
        });
    }

    private void updateTextViewContent() {
        new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                Message message = Message.obtain();
                message.what = 1;
                updateHandler.sendMessage(message);
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        updateHandler.removeCallbacksAndMessages(null);
    }
}

Create a Handler in the main thread, then start a worker thread and wait for the worker thread to sleep for 4000 milliseconds, issue a Message and then update the content of TextView in the main thread.

Source code analysis of message mechanism

Combining the above examples, let's understand these issues one by one:

The message sent in the child thread, through the following code:

Message message = Message.obtain();
message.what = 1;
updateHandler.sendMessage(message);

Trace the source code of sendMessage:

updateHandler.sendMessage(message);

I found that after a series of calls to sendMessage->sendMessageDelayed->sendMessageAtTime, I finally came to the method sendMessageAtTime.

Take a peek below:

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

In sendMessageAtTime, the Message is enqueued, that is, it is put into the MessageQueue, which is a message queue implemented by a linked list.

The enqueueMessage(queue, msg, uptimeMillis) method is called in sendMessageAtTime, and the enqueue operation is performed in this method:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

The real enqueue operation goes to the MessageQueue, and the code is as follows:

boolean enqueueMessage(Message msg, long when) {


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

See prev=p, p = p.next, msg.next=p, prev.next=msg, isn't this a linked list? But it feels very strange, why is the name MessageQueue?

So far:

The message has been sent and stored in the MessageQueue waiting to be processed, so the question is:
1. Who handles the MessageQueue?
2. How was it handled?

At this time, Looper will make a grand debut!

Created our Handler in the main thread

updateHandler = new UpdateHandler(MainActivity.this);

Trace the source code, the default constructor of Handler is called here. The source code is as follows:

/**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    public Handler() {
        this(null, false);
    }

The annotation tells us:

  1. The default construction method has been associated with a Looper of the current thread. The current thread is the main thread, then the Looper of the main thread is used. When ActivityThread starts, a mainthreadLooper has been prepared for us.
  2. If it is a thread we started by ourselves, if we do not prepare the Looper ourselves, an exception will be thrown: Can't create handler inside thread that has not called Looper.prepare()
    Under the source code, there is no secret, this is the source of this exception, Look at the code:
public Handler(Callback callback, boolean async) {
        //删掉部分无关代码
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        ......
    }

When the main thread starts, a default Looper used by the main thread has been prepared for us. In ActivityThread, there is the following code:

public static void main(String[] args) {
        ......

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        ......
    }

The two lines of code in it can help us reveal the secrets - the two problems mentioned above:

  1. Who handles the messages in the MessageQueue?
  2. How was it handled?

The code in it:

Looper.prepareMainLooper();
...
if (sMainThreadHandler == null) {
   sMainThreadHandler = thread.getHandler();
}
Looper.loop();

That's why, we created a Handler in the OnCreate method of the Activity but did not call Looper.prepre() and Looper.loop() but there is no problem, using the main thread Looper.

Let's take a look at two methods, first look at prepareMainLooper:

    //为当前线程创建一个Looper,Android环境已经为你准备好了,不需要再调用这个方法。 
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

The prepare(false) method is called in this method:

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建一个Looper,并设置给ThreadLocal
        sThreadLocal.set(new Looper(quitAllowed));
    }

At this time, ThreadLocal came out, and here we ask the question:

  1. What is ThreadLocal?
  2. What does he do?
    For the time being, press the table, and see the results later!

I'm curious about what the new Looper(quitAllowed) code does, click to see:

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

For a private method, we cannot create a new Looper, which contains a MessageQueue and the current thread! Because the current thread is the main thread, both MessageQueue and Looper are in the main thread.

At the end of the prepareMainLooper method, the myLooper() method is called:

/**
     * 返回当前线程关联的Looper对象,如果当前线程没有关联Looper对象那么就返回null.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

At this point, the Looper.prepare() method has been analyzed, and the Looper has been prepared for the main thread.

Next, look at the Looper.loop() method:

 /**
     *  轮询当前线程的消息队列。在轮询完成之后确保调用了quit()方法。
     */
    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(); // 可能会阻塞,从MessageQueue中获取下一个消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

           ......
            try {
                //msg.target 就是Handler,从MessageQueue中获取到的Message,被分发给了Handler中的dispatchMessage方法
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

           ......
        }
    }

In the Looper.loop() method, we find the answer:

Blocking access to the Message in the message queue. Then, call dispatchMessage in Handler to distribute Message.

Let's take a look at the code of dispatchMessage:

    /**
     * 在这儿处理系统Message
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

Haha, HandlerMessage looks familiar, there should be applause here!

    /**
     * 子类应该实现这个方法,来接收消息
     */
    public void handleMessage(Message msg) {
    }

The final message is handled in the handlerMessage method of the subclass!

In this way, the source code of the message mechanism in Android has been analyzed. Let's answer the questions raised at the beginning!

Problem revealed

1. Where is the message sent?

The message sent in the child thread, through the following code:

Message message = Message.obtain();
message.what = 1;
updateHandler.sendMessage(message);

2. Where is the message sent?

Message is sent to MessageQueue, a message queue!

3. How is the message processed in the end?

The Message is taken out of the MessageQueue by the Loop method of Looper with the help of the next() method, and distributed to the dispatchMessage method in the Handler for processing. This is a typical producer-consumer model!

4. How to convert the message sent by the child thread to the main thread?

The child thread sends the Message to the MessageQueue, the MessageQueue is initialized in the Looper, and the Looper is in the main thread, so the MessageQueue is also in the main thread, and the message is transformed into the main thread.

5. Where did Looper come from?

Looper in the main thread is created by Looper.preperMainLooper() when ActivityTread is created

6. Where is the loop method of Looper called?

The Loop method in the main thread is called in the main method of ActivityThread. If it is the loop method in its own thread, it needs to be called manually.

7. What is the function of each of the above categories?

  • Handler, the message processing mechanism in Android
  • Looper, message pump, used to read messages from MessageQueue
  • MessageQueue, message queue, internally implemented with a linked list
  • Message, the communication carrier between the main thread and child threads
  • ThreadLocal: Each thread can directly access data into ThreadLocal, and the data is isolated and does not interfere with each other, which is used for data isolation of threads.

8. Where is the Handler (main thread or child thread)?

Handler is created in main thread, so Handler is in main thread

9. Where is the Looper (main thread or child thread)?

Looper is created in main thread, so Looper is in main thread

10. Where is the MessageQueue (main thread or child thread)?

MessageQueue is initialized in Looper's constructor, so MessageQueue is also in the main thread

11. Where is ThreadLocal (main thread or child thread)?

In Looper, it is used to access Looper, so it is also in the main thread

At this point, the source code of the Android message mechanism is analyzed!

Under the source code, there are no secrets! One word, clear!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325170283&siteId=291194637