introduce
The previous content introduces Handler and explains how to use handler, but we don't know its implementation principle. This article analyzes how to implement it from the perspective of source code.
First of all, we need to know the relationship between Handler, Looper, and Message Queue
- Handler encapsulates the sending of messages and is also responsible for receiving and canceling them. It will be related to Looper internally.
- Looper message encapsulated load, which contains MessageQueue, is responsible for fetching messages from MessageQueue and then handing them over to Handler for processing
- MessageQueue is a message queue that is responsible for storing messages. It will be stored when messages come. Looper will loop through the message from the MessageQueue.
Source code analysis
When we new a Handler object, see what is done in its constructor.
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((() || () || ()) && (() & ) == 0) { (TAG, "The following Handler class should be static or leaks might occur: " + ()); } } mLooper = (); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called ()"); } mQueue = ; mCallback = callback; mAsynchronous = async; }
From the source code, we see that it will call the method to get a Looper object, and then get the MessageQueue object from the Looper object.
Looper myLooper()
Follow in and see what the() method does. This is a static method that can be called directly by class name and method name.
public static @Nullable Looper myLooper() { return (); }
This method has a line of code to get a Looper object from sThreadLocal. sThreadLocal is a ThreadLocal object that can store variables in a thread. The underlying layer is ThreadLocalMap. Since it is a Map type, you must first set a Looper object, and then we can get a Looper object from the sThreadLocal object.
ActivityThread main()
Speaking of this, I have to introduce to you a new class ActivityThread. The ActivityThread class is the initial class of the Android APP process, and its main function is the entrance to the APP process. Let's see what this main function does.
public static final void main(String[] args) { ------ (); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); (false); if (false) { ().setMessageLogging(new LogPrinter(, "ActivityThread")); } (); ----- }
In the second line of code, the() method is called, and in line 13, the() method is called.
Looper prepareMainLooper()
Continue to follow up with the() method, in which the first line of code calls the internal prepare method. prepareMainLooper is a bit like the getInstance method in singleton mode, except that getInstance will return an object at that time, and prepareMainLooper will create a new Looper object and store it in sThreadLocal.
public static void prepareMainLooper() { prepare(false); synchronized () { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
Looper prepare()
Continue to follow up on the prepare method, look at the 5th line of code, create a new Looper object, and call the method to save the Looper object. Seeing this, I think you guys who are smart must understand why when calling the() method in the new Handler object, you can get the Looper object from the sThreadLocal object.
private static void prepare(boolean quitAllowed) { if (() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } (new Looper(quitAllowed)); }
Looper construction method
At the beginning of the article, we talked about the inclusion of MessageQueue within Looper. In fact, when the new Looper object is new, a MessageQueue object is new.
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = (); }
Looper loop()
The ActivityThread class main method calls two methods of Looper. We explained prepareMainLooper() before, and now we look at the second method loop().
public static void loop() { final Looper me = myLooper();//Get Looper object if (me == null) { throw new RuntimeException("No Looper; () wasn't called on this thread."); } final MessageQueue queue = ;//Get MessageQueue object from Looper object // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. (); final long ident = (); for (;;) {//The dead loop keeps traversing messages from the MessageQueue Message msg = (); // might block if (msg == null) { return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = ; if (logging != null) { (">>>>> Dispatching to " + + " " + + ": " + ); } final long traceTag = ; if (traceTag != 0 && (traceTag)) { (traceTag, (msg)); } try { //Call the dispatchMessage method of the handler and hand over the message to the handler for processing (msg); } finally { if (traceTag != 0) { (traceTag); } } if (logging != null) { ("<<<<< Finished to " + + " " + ); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = (); if (ident != newIdent) { (TAG, "Thread identity changed from 0x" + (ident) + " to 0x" + (newIdent) + " while dispatching to " + ().getName() + " " + + " what=" + ); } (); } }
There are many codes for this method. I commented on the code. In fact, it is a dead loop, which keeps getting messages from MessageQueue. If the message is retrieved, the (msg) line of code will be executed, which is the handler. In fact, it is called the handler's dispatchMessage method, and then pass the message retrieved from the MessageQueue in.
Handler dispatchMessage()
public void dispatchMessage(Message msg) { //If the callback is not empty, it means that the message is sent as a Runnable object. if ( != null) { handleCallback(msg); } else { if (mCallback != null) {//This is used to intercept messages if ((msg)) { return; } } handleMessage(msg);//Finally call the handleMessage method we rewritten } }
This method performs the final processing of the message. If it is a post type, call the handleCallback method to process it, and if it is a message sent by sendMessage. Let's see if we intercept messages, if we don't end up calling the handleMessage method to handle.
Handler handleCallback()
After seeing this, we know why the code executed by posting a Runnable object is in the main thread, because the underlying layer does not have a thread opened at all, just the run method is called.
private static void handleCallback(Message message) { (); }
We started with creating handler objects, creating Loopers, and creating MessageQueue. Now let’s analyze how to add Message to MessageQueue when we call the post and sendMessage methods.
Handler post()
The getPostMessage method is called and the Runnable is passed in.
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
Handler getPostMessage()
First, call the() method and take out a Message object. This method has been discussed before, and then assign the Runnable object to the callback attribute of the Message object. After seeing this, we also understand why the dispatchMessage method must first determine whether the callback is empty.
private static Message getPostMessage(Runnable r) { Message m = (); = r; return m; }
Handler enqueueMessage()
The sendMessageDelayed method is called in the post method, and the end is called the enqueueMessage method, so I will directly look at the source code of the enqueueMessage method. The first line of code assigns the handler itself to the target property of the messgae object. Then call the enqueueMessage method of MessageQueue to add the current Messgae.
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { = this; if (mAsynchronous) { (true); } return (msg, uptimeMillis); }
Summarize
Summary: The handler is responsible for sending messages, and the Looper is responsible for receiving messages sent by the Handler and directly sending the message back to the Handler itself. MessageQueue is a container that stores messages.
This is the article about the detailed explanation of the relationship case between Android Handle (Looper, Handler and Message). For more related content about Android Handle (Looper, Handler and Message, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!