Source code analysis of handler mechanism
I always think I have a thorough understanding of handler mechanism. An accidental opportunity found that after working for a period of time, my wife forgot all about it and combed it again today. I hope it will be more in-depth.
Summarize a few core classes first, so that you don't get a clue when you look at the record later.
ActivityThread
Loop
ThreadLocal
MessageQueue
Message
Handler
Now that we have a good directory, what we need to do is to define what these classes are used for.
What is ActivityThread?
Some people say that the main thread or UI thread is wrong. Let's look at the definition of ActivityThread.
public final class ActivityThread {
ActivityThread is just a simple class and thread. Thread seems to have nothing to do with it.
So why would someone say ActivityThread is the main thread? The simple reason is that Zyget will be fork ed by a new process, which is the process that app runs when app starts. The process will load the ActivitThread class after it has been built, so according to the java foundation, we will run the static main function mian with a new ActivityThread, so ActivityThread starts to run in the main thread (here may also be my understanding is not in place, if there is a good way to understand also ask for advice).
Here's the main 5.0 source code. If you're upset, you can skip the code block directly because there are four sentences in it that are useful to us.
5184 public static void main(String[] args) {
5185 SamplingProfilerIntegration.start();
5186
5187 // CloseGuard defaults to true and can be quite spammy. We
5188 // disable it here, but selectively enable it later (via
5189 // StrictMode) on debug builds, but using DropBox, not logs.
5190 CloseGuard.setEnabled(false);
5191
5192 Environment.initForCurrentUser();
5193
5194 // Set the reporter for event logging in libcore
5195 EventLogger.setReporter(new EventLoggingReporter());
5196
5197 Security.addProvider(new AndroidKeyStoreProvider());
5198
5199 // Make sure TrustedCertificateStore looks in the right place for CA certificates
5200 final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
5201 TrustedCertificateStore.setDefaultUserDirectory(configDir);
5202
5203 Process.setArgV0("<pre-initialized>");
5204
5205 Looper.prepareMainLooper();
5206
5207 ActivityThread thread = new ActivityThread();
5208 thread.attach(false);
5209
5210 if (sMainThreadHandler == null) {
5211 sMainThreadHandler = thread.getHandler();
5212 }
5213
5214 AsyncTask.init();
5215
5216 if (false) {
5217 Looper.myLooper().setMessageLogging(new
5218 LogPrinter(Log.DEBUG, "ActivityThread"));
5219 }
5220
5221 Looper.loop();
5222
5223 throw new RuntimeException("Main thread loop unexpectedly exited");
5224 }
5225}
An ActivityThread is created in line 5207, 5208 and the current data is initialized into ActivityThread
Then focus on
5205 Loper. prepareMainLooper (); it creates a Looper instance and saves it in ThreadLocal.
Looper.loop(); opens Looper's loop for processing
This is the end of the process, and the main thread has entered a dead cycle; PS: After the life cycle management, ui processing is processed in the loop.
What is Loop?
The Loop core is a dead loop for looping ** to process ** MessageQueue information
Notice the endless cycle and processing of the words I use, and you'll see why I use them.
Let's start with the concept that there is no Looper by default in every thread. If you want to use it, you have to initialize a Looper for the thread itself, and the main thread initializes a Looper when it is created. That's why the main thread can use the Handler sometimes.
In the previous section, I said that 5027 and 5028 need attention.
- Let's start with Looper.prepareMainLooper() here in 5027.
87 public static void prepareMainLooper() {
88 prepare(false);
89 synchronized (Looper.class) {
90 if (sMainLooper != null) {
91 throw new IllegalStateException("The main Looper has already been prepared.");
92 }
93 sMainLooper = myLooper();
94 }
95 }
Initialize it in the first sentence prepare(false)
74 private static void prepare(boolean quitAllowed) {
75 if (sThreadLocal.get() != null) {
76 throw new RuntimeException("Only one Looper may be created per thread");
77 }
78 sThreadLocal.set(new Looper(quitAllowed));
79 }
The prepare function also tells us very clearly that a new non-existent Looper is placed in ThreadLocal.
186 private Looper(boolean quitAllowed) {
187 mQueue = new MessageQueue(quitAllowed);
188 mThread = Thread.currentThread();
189 }
Looper created a MessageQueue for itself and saved the current thread, so Looper was created.
- Then the Looper.loop() of 5221 is promptly opened; Looper's loop is opened for processing.
Another wavelength code is just the same as the one you don't want to see. It's useful not to read a few sentences (in fact, there aren't many lines besides comments).
109 public static void loop() {
110 final Looper me = myLooper();
111 if (me == null) {
112 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
113 }
114 final MessageQueue queue = me.mQueue;
115
116 // Make sure the identity of this thread is that of the local process,
117 // and keep track of what that identity token actually is.
118 Binder.clearCallingIdentity();
119 final long ident = Binder.clearCallingIdentity();
120
121 for (;;) {
122 Message msg = queue.next(); // might block
123 if (msg == null) {
124 // No message indicates that the message queue is quitting.
125 return;
126 }
127
128 // This must be in a local variable, in case a UI event sets the logger
129 Printer logging = me.mLogging;
130 if (logging != null) {
131 logging.println(">>>>> Dispatching to " + msg.target + " " +
132 msg.callback + ": " + msg.what);
133 }
134
135 msg.target.dispatchMessage(msg);
136
137 if (logging != null) {
138 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
139 }
140
141 // Make sure that during the course of dispatching the
142 // identity of the thread wasn't corrupted.
143 final long newIdent = Binder.clearCallingIdentity();
144 if (ident != newIdent) {
145 Log.wtf(TAG, "Thread identity changed from 0x"
146 + Long.toHexString(ident) + " to 0x"
147 + Long.toHexString(newIdent) + " while dispatching to "
148 + msg.target.getClass().getName() + " "
149 + msg.callback + " what=" + msg.what);
150 }
151
152 msg.recycleUnchecked();
153 }
154 }
Focus
final Looper me = myLooper(); gets the Loper of the current thread
121 for (;;) Dead loop to process information in message
Message msg = queue.next() of 122; get information about messageQueue (how do we get it here and then MessageQueue again)
135 msg.target.dispatchMessage(msg); Processing information (how? It must be handler. It's handler in tag. After what handler does, handler will talk about it in detail.
160 public static Looper myLooper() {
161 return sThreadLocal.get();
162 }
myLooper was obtained in ThreadLocal
What is ThreadLocal?
Every time ThreadLocal looks at the name, the first response is the xx thread. It's not easy to call yourself a thread here, "Your sister, threads are everything. He's clearly a storage class." Different copies of different threads of the storage class do not affect each other, and the scope of the entire thread is within the scope of the whole thread.
- Let's start with set
179 public void set(T value) {
180 Thread t = Thread.currentThread();
181 ThreadLocalMap map = getMap(t);
182 if (map != null)
183 map.set(this, value);
184 else
185 createMap(t, value);
186 }
I didn't look at it until I saw it. I saved it with map. But that's not the case... static class ThreadLocalMap {.... Nima Map is not directly related to Map class or an internal class. You fucking play with me. If you cut out the interview and ask me the details, I will answer by Map instead of fucking? In fact, it was actually used by an interviewer.
416 private void set(ThreadLocal key, Object value) {
417
418 // We don't use a fast path as with get() because it is at
419 // least as common to use set() to create new entries as
420 // it is to replace existing ones, in which case, a fast
421 // path would fail more often than not.
422
423 Entry[] tab = table;
424 int len = tab.length;
425 int i = key.threadLocalHashCode & (len-1);
426
427 for (Entry e = tab[i];
428 e != null;
429 e = tab[i = nextIndex(i, len)]) {
430 ThreadLocal k = e.get();
431
432 if (k == key) {
433 e.value = value;
434 return;
435 }
436
437 if (k == null) {
438 replaceStaleEntry(key, value, i);
439 return;
440 }
441 }
442
443 tab[i] = new Entry(key, value);
444 int sz = ++size;
445 if (!cleanSomeSlots(i, sz) && sz >= threshold)
446 rehash();
447 }
Looking at the code, we know that there is a table array stored in it!!!
You should know 425 key. threadLocalHashCode & (len-1) when you look at the hashMap source code; what you do is take the remainder and use the hashCode of threads to calculate it.
The overall function of this code block is to use the remainder as the following table. When conflicts occur, the subscript is moved backwards from the increase to resolve conflicts. At the end of the judgment, and then determine whether rehash expansion. In principle, it is similar to hashMap, but hashMap resolves conflicts by adding lists to arrays. Conflicts are resolved in different ways. I'm not going to elaborate here.
ps: ThreadLocal essentially means that when a thread calls, it gets not one variable with different variables corresponding to the current process, thus realizing the isolation of thread variables, and the scope of variables in the thread becomes the whole thread.
What is MessageQueue?
* MessageQueue* Manages Message Queues From the Looper code above, you can see that each Looper holds a MessageQueue object
The most important method of a message queue is definitely insertion and extraction.
- enqueueMessage() method --> insert
- next() method ---> take out and remove
The first thing to see is how to put it in MessageQueue, and why to put it in this way, you can see handler after you see it.
boolean enqueueMessage(Message msg, long when) {
316 if (msg.target == null) {
317 throw new IllegalArgumentException("Message must have a target.");
318 }
319 if (msg.isInUse()) {
320 throw new IllegalStateException(msg + " This message is already in use.");
321 }
322
323 synchronized (this) {
324 if (mQuitting) {
325 IllegalStateException e = new IllegalStateException(
326 msg.target + " sending message to a Handler on a dead thread");
327 Log.w("MessageQueue", e.getMessage(), e);
328 msg.recycle();
329 return false;
330 }
331
332 msg.markInUse();
333 msg.when = when;
334 Message p = mMessages;
335 boolean needWake;
336 if (p == null || when == 0 || when < p.when) {
337 // New head, wake up the event queue if blocked.
338 msg.next = p;
339 mMessages = msg;
340 needWake = mBlocked;
341 } else {
342 // Inserted within the middle of the queue. Usually we don't have to wake
343 // up the event queue unless there is a barrier at the head of the queue
344 // and the message is the earliest asynchronous message in the queue.
345 needWake = mBlocked && p.target == null && msg.isAsynchronous();
346 Message prev;
347 for (;;) {
348 prev = p;
349 p = p.next;
350 if (p == null || when < p.when) {
351 break;
352 }
353 if (needWake && p.isAsynchronous()) {
354 needWake = false;
355 }
356 }
357 msg.next = p; // invariant: p == prev.next
358 prev.next = msg;
359 }
360
361 // We can assume mPtr != 0 because mQuitting is false.
362 if (needWake) {
363 nativeWake(mPtr);
364 }
365 }
366 return true;
367 }
The first part is to determine that the core of some states is that 336 lines begin to put message into the list, and this position is put into the sum for(;) block of code, which is not a dead loop. Because the internal is a single linked list, the single linked list is not the end of the loop list, so this for will not be a dead loop.
The purpose of this is to insert the message into the end of the list.
Then take it out and remove it. Is this a familiar way to look at it?
The reason is that the Looper.loop() method in the Main function of ActivityThread has been called.
- The following is a large source code, which involves two major parts: the Native layer and the Java layer. Here, I have not talked too much about the Native layer. Let's just look at the Java layer.
127 Message next() {
128 // Return here if the message loop has already quit and been disposed.
129 // This can happen if the application tries to restart a looper after quit
130 // which is not supported.
131 final long ptr = mPtr;
132 if (ptr == 0) {
133 return null;
134 }
135
136 int pendingIdleHandlerCount = -1; // -1 only during first iteration
137 int nextPollTimeoutMillis = 0;
138 for (;;) {
139 if (nextPollTimeoutMillis != 0) {
140 Binder.flushPendingCommands();
141 }
142
143 nativePollOnce(ptr, nextPollTimeoutMillis);
144
145 synchronized (this) {
146 // Try to retrieve the next message. Return if found.
147 final long now = SystemClock.uptimeMillis();
148 Message prevMsg = null;
149 Message msg = mMessages;
150 if (msg != null && msg.target == null) {
151 // Stalled by a barrier. Find the next asynchronous message in the queue.
152 do {
153 prevMsg = msg;
154 msg = msg.next;
155 } while (msg != null && !msg.isAsynchronous());
156 }
157 if (msg != null) {
158 if (now < msg.when) {
159 // Next message is not ready. Set a timeout to wake up when it is ready.
160 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
161 } else {
162 // Got a message.
163 mBlocked = false;
164 if (prevMsg != null) {
165 prevMsg.next = msg.next;
166 } else {
167 mMessages = msg.next;
168 }
169 msg.next = null;
170 if (false) Log.v("MessageQueue", "Returning message: " + msg);
171 return msg;
172 }
173 } else {
174 // No more messages.
175 nextPollTimeoutMillis = -1;
176 }
177
178 // Process the quit message now that all pending messages have been handled.
179 if (mQuitting) {
180 dispose();
181 return null;
182 }
183
184 // If first time idle, then get the number of idlers to run.
185 // Idle handles only run if the queue is empty or if the first message
186 // in the queue (possibly a barrier) is due to be handled in the future.
187 if (pendingIdleHandlerCount < 0
188 && (mMessages == null || now < mMessages.when)) {
189 pendingIdleHandlerCount = mIdleHandlers.size();
190 }
191 if (pendingIdleHandlerCount <= 0) {
192 // No idle handlers to run. Loop and wait some more.
193 mBlocked = true;
194 continue;
195 }
196
197 if (mPendingIdleHandlers == null) {
198 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
199 }
200 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
201 }
202
203 // Run the idle handlers.
204 // We only ever reach this code block during the first iteration.
205 for (int i = 0; i < pendingIdleHandlerCount; i++) {
206 final IdleHandler idler = mPendingIdleHandlers[i];
207 mPendingIdleHandlers[i] = null; // release the reference to the handler
208
209 boolean keep = false;
210 try {
211 keep = idler.queueIdle();
212 } catch (Throwable t) {
213 Log.wtf("MessageQueue", "IdleHandler threw exception", t);
214 }
215
216 if (!keep) {
217 synchronized (this) {
218 mIdleHandlers.remove(idler);
219 }
220 }
221 }
222
223 // Reset the idle handler count to 0 so we do not run them again.
224 pendingIdleHandlerCount = 0;
225
226 // While calling an idle handler, a new message could have been delivered
227 // so go back and look again for a pending message without waiting.
228 nextPollTimeoutMillis = 0;
229 }
230 }
It's time for code analysis again.
In line 143, nativePollOnce (ptr, nextPoll Timeout Millis); this is a nativelayer that handles more than one thing. First, it concludes: "native layer also has a similar message mechanism, which handles natives first, then java layer. There are also distinctions in java layer, that is, asynchronous and synchronous. Final deal with idle handler"
Here's one. address Look, there's a mention in it.The comments in lines 150-156 see 151 refer to being opened by a barrier to get the first asynchronous Message. Here is to find the asynchronous Message and then process the Message
Here Message is also divided into synchronous and asynchronous. What is the difference between synchronous and asynchronous? This is what the Message says later.
Careful friends may find that 150 lines of code are judged by msg.target==null, but before mentioning that a mouthful of msg.target should contain handler's ah null. Here's a Barrier concept.
Barrier is a special message whose target is null. (Only Barrier's target can be null, and if we try to set Message's target to null, we will report an exception.) What's the effect? Used to distinguish synchronous messages from interceptors and release asynchronous messages?
Here's another one. address To expand, there are introductions, although not very detailed, can be used as an introduction reference.The function of 157-176 is to compare the current time with the task time to see if the task time has arrived. If it arrives, it will return to the current task. If not, it will continue to go down the dead cycle.
178-182 You can see from the comment that the value to exit mQuitting is set in quit(), of course, before setting it, you need to determine whether mQuitAllowed==true is set at the beginning of creation.
After that, it runs the acquisition of idle messages. It's understandable that there are also some functions in Message Queue that are specifically designed to set up idle messages, not to mention here.
PS: All in all, it belongs to the whole messageQueue acquisition process. Suddenly, the more it is written, the more unknown it is. Otherwise, one handler will lose its head and the others will play a ball. Then there's time for improvement.
What is Message?
Message messages are stored in MessageQueue as a medium of storage type for acquisition and processing.
There is a comment on the construction method of Message /** Constructor (but the preferred way to get a Message is to call {@link get () Message. get ()}).*/
Message does not recommend using new to create a message. It suggests using obtain to create a message. In short, there will be some initialization methods in the obtain method to find the m.target=h which is useful to us and is also available in almost every obtainment overload method.
- m stands for message
- h stands for Handler
This also proves that the handler is in the msg.target target. dispatchMessage (msg) in the Looper.loop() method and that msg.target.dispatchMessage(msg) will distribute msg to the handler's dispatchMessage for distribution processing.
118 /**
119 * Return a new Message instance from the global pool. Allows us to
120 * avoid allocating new objects in many cases.
121 */
122 public static Message obtain() {
123 synchronized (sPoolSync) {
124 if (sPool != null) {
125 Message m = sPool;
126 sPool = m.next;
127 m.next = null;
128 m.flags = 0; // clear in-use flag
129 sPoolSize--;
130 return m;
131 }
132 }
133 return new Message();
134 }
In this case, I'll find out that the Message Processing MessageQueue has an sPool global information pool to maintain. So the teacher is knocking on the blackboard. What is the use of this information pool? Its function is reuse. As can be seen from the above code block, sPool is a linked list structure. What's stored in this list? What's saved is the reusable Message. After reading the following, you can see that the list must have a memory and a acquisition process. The code above is taken. So what's there? It's below when looper.loop() processes dispatchMessage. Use the following functions.
114 private static final int MAX_POOL_SIZE = 50;
...
291 void recycleUnchecked() {
292 // Mark the message as in use while it remains in the recycled object pool.
293 // Clear out all other details.
294 flags = FLAG_IN_USE;
295 what = 0;
296 arg1 = 0;
297 arg2 = 0;
298 obj = null;
299 replyTo = null;
300 sendingUid = -1;
301 when = 0;
302 target = null;
303 callback = null;
304 data = null;
305
306 synchronized (sPoolSync) {
307 if (sPoolSize < MAX_POOL_SIZE) {
308 next = sPool;
309 sPool = this;
310 sPoolSize++;
311 }
312 }
313 }
From the last 306 to 313 lines, you can see that when the number of sPools is not greater than 50, the used message will be put back in the sPool and put in the first place. When it's over 50, it's not forgotten that it's stored in pool. Because of space constraints, I don't write much. Look here.
So most of the core stuff of message is done.
What is Handler?
Handler is translated as a handler. It is obvious from translation that handler carries message processing. If you have read the above chapters, you must know handler.dispatchMessage handles messages.
93 public void dispatchMessage(Message msg) {
94 if (msg.callback != null) {
95 handleCallback(msg);
96 } else {
97 if (mCallback != null) {
98 if (mCallback.handleMessage(msg)) {
99 return;
100 }
101 }
102 handleMessage(msg);
103 }
104 }
From the above code, it can be seen that if there is handler Callback, it will be handler Message to deal with this method, I will not say, after all, most people who have used handler have certainly realized this method. Then the logic for dealing with this is cleared, and looper opens the loop. MessageageQueue is used to get the message, which is then retrieved from message.target and handler calls dispatchMessage to process the message based on the resulting handler.
So it feels like there's something missing. That's how messages are inserted into messageQueue.
When we use handler message mechanism, we must have been to post or post method postDelayed and other methods, in fact, the internal are gathered in one place. Let's take post as an example
324 public final boolean post(Runnable r)
325 {
326 return sendMessageDelayed(getPostMessage(r), 0);
327 }
There's a place where it's easy to ignore the getPostMessage method.
725 private static Message getPostMessage(Runnable r) {
726 Message m = Message.obtain();
727 m.callback = r;
728 return m;
729 }
See, the legendary Message has been created.
565 public final boolean More ...sendMessageDelayed(Message msg, long delayMillis)
566 {
567 if (delayMillis < 0) {
568 delayMillis = 0;
569 }
570 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
571 }
Here, the sendMessageAtTime is invoked after the time is converted to the corresponding departure time, and this method is the final invocation method of post and other methods.
592 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
593 MessageQueue queue = mQueue;
594 if (queue == null) {
595 RuntimeException e = new RuntimeException(
596 this + " sendMessageAtTime() called with no mQueue");
597 Log.w("Looper", e.getMessage(), e);
598 return false;
599 }
600 return enqueueMessage(queue, msg, uptimeMillis);
601 }
To insert into MessageQueue
626 private boolean More ...enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
627 msg.target = this;
628 if (mAsynchronous) {
629 msg.setAsynchronous(true);
630 }
631 return queue.enqueueMessage(msg, uptimeMillis);
632 }
Before inserting a message like MessageQueue, the target of the message is assigned and handler is passed in. The code break also confirms what was said before. You should know the process after that.
Summarize the overall process of handler
***sendMessage->sendMessageDelayed->sendMessageAtTime->enqueueMessage
At this point, the distribution mechanism of the whole message flow is to sort out a flow chart, but there is no place to access the picture for the time being before adding it.
Expanding a Knowledge Point
1161 private class H extends Handler in ActivityThread
It defines some life cycle related stuff. Look at the right and then have time to write the app startup process.
Use: How to use your own Looper? Official examples
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
If you have the patience to write so much code, you should have a holistic understanding of the messaging mechanism.