The message loop has 4 important classes: Handler, Message, Looper, and MessageQueue.
handler is used to send and process messages.
Message is the carrier of the message.
MessageQueue is a message queue. Since it is a queue, there are processing to join and dequeue.
Looper creates a message loop. Continuously read messages from MessageQueue and distribute them to the corresponding Handler for processing.
2. We all know that the main function is the entrance to Java programs, and android programs are no exception.
The only entry to Android App is the main function in ActivityThread. This function is called through reflection after Zygote creates the app process.
When an App starts, the main method will be executed first, in the ActivityThread and main methods,
public static void main(String[] args) { //Create a message loop (); //Create ActivityThread object ActivityThread thread = new ActivityThread(); //Create Application and start MainActivity (false, startSeq); //Make the message loop run (); //Exception was thrown and the main thread's Looper exited unexpectedly. //So the for loop in loop needs to be blocked here. Once the main function is executed, the process will exit. //And you have to extract messages and process messages. throw new RuntimeException("Main thread loop unexpectedly exited"); }
3. First, let’s see how Looper is created.
(); public static void prepareMainLooper() { prepare(false); //The synchronization method ensures that an sMainLooper is assigned only once. synchronized () { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //Create a Looper object and save it in sThreadLocal. ThreadLocal is also a very important knowledge point. private static void prepare(boolean quitAllowed) { //A thread can only have one Looper. if (() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } (new Looper(quitAllowed)); } public static @Nullable Looper myLooper() { return (); } //Create MessageQueue in Looper private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = (); }
At this point, the created code has been executed.
To sum up, when the app starts, a Looper will be created, and it is guaranteed that only one Looper can be created by a thread.
While creating Looper, it also creates a message queue MessageQueue. These are all preparations for the message loop.
By passing, the message loop starts to run.
(); /** * Run the message queue in this thread. */ public static void loop() { for (; ; ) { //Extract messages from message loop. Message blocks here. Message msg = (); // might block //target-->Hold the reference of the handler. (msg); } }
4. Insert message in enqueueMessage() and extract message methods next() in MessageQueue.
Insert message //() gets a message from the Message message cache pool. (()); public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0); } //The delay message we send is written into the message loop, the current time + delay time, and a certain time in the future. public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, () + delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //this==handler, here the handler is assigned. = this; if (mAsynchronous) { (true); } return (msg, uptimeMillis); }
//Save messages in MessageQueue using a single-line linked list. //In the single linked list of this message, it is sorted from small to large according to the time of the message execution. //mMessages is the first message for this single linked list. boolean enqueueMessage(Message msg, long when) { //The message of handler==null cannot be sent through sendMessage. if ( == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { (); = when; // Assign the first message in the linked list to p Message p = mMessages; boolean needWake; //If p==null means there is no message to be executed in the message list. //If when==0 means that the newly added message will be executed immediately, so it must be ranked at the head of the list //If when<, it means that the newly added message is executed first than the first message in the message queue, so it should also be placed in the header. if (p == null || when == 0 || when < ) { // New head, wake up the event queue if blocked. // Assign the original header message to the next of the new message = p; // Assign the newly added message to mMessage. Because mMessages variable is used to save the first message in the message list. mMessages = msg; } else {//If when>=, you need to traverse the message queue and insert the newly added message into the middle of the queue. Message prev; for (;;) { prev = p;// Assign the current value to the previous one p = ;// Assign the next value to the current one //If p==null means that it has been traversed to the end of the linked list. //If the new message time is less than p's when. Then this message should be inserted after prev and before p. if (p == null || when < ) { break; } } //Add new messages before p and after prev = p; // invariant: p == = msg; } } return true; }
Extract message:
Message next() { int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { (); } //Sleep starts, nextPollTimeoutMillis will be awakened next time //If it is -1, it will keep sleeping until a new message wakes up the message queue. nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. //Start traversal of the message queue and return the found message. final long now = (); Message prevMsg = null; //Message queue, header message //If msg! =null, but ==null, sendMessage is not allowed to send messages with handler null. //target==null message is sent by the system. First, send a synchronization barrier message, and then send an asynchronous message until isAsynchronous = true. //The purpose of doing this ensures that the asynchronous message has higher priority and is extracted from the message queue first. if (msg != null && == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = ; } while (msg != null && !());// Until isAsynchronous = true, that is, the asynchronous message of the synchronization barrier was found } } if (msg != null) { if (now < ) {//The current time is less than the time when the message is executed, record the difference // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) ( - now, Integer.MAX_VALUE); } else {//If the current time is not less than the execution time of the message being fetched, the message is extracted from the message queue and returned to it // Got a message. mBlocked = false; if (prevMsg != null) { //Point the next message of the previous message to the next message of the current message. = ; } else { //If the previous message is null, it means that the current message header is //Point the message queue header message to the next message that is currently extracted. mMessages = ; } //The next assignment of the found message null is detached from the original message queue. = null; if (false) ("MessageQueue", "Returning message: " + msg); return msg; } } else {//If msg==null // No more messages. nextPollTimeoutMillis = -1; } } } }
The handler will involve native code. The epoll mechanism used in the native layer is shared in depth later.
This involves the concept of a message barrier, and you have the opportunity to write an article separately to share.
//Synchronous barrier messages void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; //Send a synchronization barrier message mTraversalBarrier = ().postSyncBarrier(); ( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); } }
5. At this point, the overall process of the entire message loop has been completed. But there are many interview questions about handler.
For example, why does handler cause Activity memory leak? How to solve it?
The essence of memory leaks is that long-declaration cycle objects hold references to short-declaration cycle objects, resulting in short-declaration cycle objects that are no longer used but memory cannot be recycled.
We know that handler, as an inner class of Activity, holds references to external classes, so the entire reference chain is
Activity-->handler-->Message-->MessageQueue.
When the activity exits, if the message is processed, it may cause the activity to be unable to be recycled by GC, resulting in memory leaks.
(), the sent message is executed in the child thread or the main thread?
Let’s see the news pool below. The message pool is also a single necklace list with a length of 50.
The static object sPool is the header Message of the message queue.
Each time a message is retrieved, the first object in the message pool is returned.
() private static Message sPool; private static final int MAX_POOL_SIZE = 50; public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = ; = null; = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
This is the end of this article about in-depth exploration of Android Handler source code. For more related Android Handler content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!