簡述:
繼續Android Handler揭祕(一),這裏來繼續分析MessageQueue。
相關代碼:
- frameworks/base/core/java/android/os/MessageQueue.java
- frameworks/base/core/jni/android_os_MessageQueue.cpp
- frameworks/base/core/java/android/os/Message.java
正文:
先寫個test代碼,然後根據test代碼來分析代碼。
01 public void test3() {
02 Log.d(TAG, "test3 start-->");
03 HandlerThread handlerThread = new HandlerThread("Test3");
04 handlerThread.start();
05 Handler handler = new Handler(handlerThread.getLooper()) {
06 @Override
07 public void handleMessage(Message msg) {
08 Log.d(TAG, "test3 handleMessage msg=" + msg + " Time=" + SystemClock.uptimeMillis());
09 }
10 };
11 Message msg = handler.obtainMessage(0, 0, 0, "msg");
12 handler.sendMessageDelayed(msg, 3000);
13 Log.d(TAG, "test3 send Msg0=" + msg + " Time=" + SystemClock.uptimeMillis());
14 msg = handler.obtainMessage(1, 0, 0, "msg1");
15 handler.sendMessageDelayed(msg, 6000);
16 Log.d(TAG, "test3 send Msg1=" + msg + " Time=" + SystemClock.uptimeMillis());
17 Log.d(TAG, "test3 end<--");
18 }
log輸出:
2019-01-07 15:18:08.861 5819-5819/com.fy.platfromdebug D/TestHandler: test3 start-->
2019-01-07 15:18:08.865 5819-5819/com.fy.platfromdebug D/TestHandler: test3 send Msg0={ when=+3s0ms what=0 obj=msg target=com.fy.platfromdebug.TestHandler$1 } Time=177966090
2019-01-07 15:18:08.866 5819-5819/com.fy.platfromdebug D/TestHandler: test3 send Msg1={ when=+6s0ms what=1 obj=msg1 target=com.fy.platfromdebug.TestHandler$1 } Time=177966090
2019-01-07 15:18:08.867 5819-5819/com.fy.platfromdebug D/TestHandler: test3 end<--
2019-01-07 15:18:11.866 5819-5851/com.fy.platfromdebug D/TestHandler: test3 handleMessage msg={ when=-1ms what=0 obj=msg target=com.fy.platfromdebug.TestHandler$1 } Time=177969091
2019-01-07 15:18:14.869 5819-5851/com.fy.platfromdebug D/TestHandler: test3 handleMessage msg={ when=-4ms what=1 obj=msg1 target=com.fy.platfromdebug.TestHandler$1 } Time=177972094
從第11行開始分析,msg並不是寫成直接Message msg = new Message(),原因是Message裏面有做對象回收池(pool of recycled objects),有利於節省內存。handler.obtainMessage最終會調用到:
frameworks/base/core/java/android/os/Message.java
242 /**
243 * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
244 * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
245 *
246 * @param h The <em>target</em> value to set.
247 * @param what The <em>what</em> value to set.
248 * @param arg1 The <em>arg1</em> value to set.
249 * @param arg2 The <em>arg2</em> value to set.
250 * @param obj The <em>obj</em> value to set.
251 * @return A Message object from the global pool.
252 */
253 public static Message obtain(Handler h, int what,
254 int arg1, int arg2, Object obj) {
255 Message m = obtain();
256 m.target = h;
257 m.what = what;
258 m.arg1 = arg1;
259 m.arg2 = arg2;
260 m.obj = obj;
261
262 return m;
263 }
緩存的關鍵就在於255 行Message m = obtain();
frameworks/base/core/java/android/os/Message.java
122 /**
123 * Return a new Message instance from the global pool. Allows us to
124 * avoid allocating new objects in many cases.
125 */
126 public static Message obtain() {
127 synchronized (sPoolSync) {
128 if (sPool != null) {
129 Message m = sPool;
130 sPool = m.next;
131 m.next = null;
132 m.flags = 0; // clear in-use flag
133 sPoolSize--;
134 return m;
135 }
136 }
137 return new Message();
138 }
加上下面的方法,就可以知道緩存的工作原理了。
frameworks/base/core/java/android/os/Message.java
291 /**
292 * Recycles a Message that may be in-use.
293 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
294 */
295 void recycleUnchecked() {
296 // Mark the message as in use while it remains in the recycled object pool.
297 // Clear out all other details.
298 flags = FLAG_IN_USE;
299 what = 0;
300 arg1 = 0;
301 arg2 = 0;
302 obj = null;
303 replyTo = null;
304 sendingUid = -1;
305 when = 0;
306 target = null;
307 callback = null;
308 data = null;
309
310 synchronized (sPoolSync) {
311 if (sPoolSize < MAX_POOL_SIZE) {
312 next = sPool;
313 sPool = this;
314 sPoolSize++;
315 }
316 }
317 }
當有Message不再有用的時候,清空成員的值,然後判斷下,當前對象回收池裏面存的個數,如果小於MAX_POOL_SIZE,就將這個Message插入sPool鏈表,這樣這個對象會被引用起來,系統不會回收,同時sPoolSize加1。
當我們需要新建一個Message時,通過public static Message obtain()方法,從sPool鏈表裏面取出一個Message(如果sPool鏈表裏面沒有時才new一個Message),同時對sPoolSize減1。
這裏的鏈表操作都是有加鎖同步,確保多線程操作不會有問題。
分析完Message鏈表緩存機制。回到test3代碼第12行handler.sendMessageDelayed(msg, 3000),此函數最終調用至
frameworks/base/core/java/android/os/MessageQueue.java
536 boolean enqueueMessage(Message msg, long when) {
//異常判斷
537 if (msg.target == null) {
538 throw new IllegalArgumentException("Message must have a target.");
539 }
540 if (msg.isInUse()) {
541 throw new IllegalStateException(msg + " This message is already in use.");
542 }
543
//加鎖同步
544 synchronized (this) {
545 if (mQuitting) {
546 IllegalStateException e = new IllegalStateException(
547 msg.target + " sending message to a Handler on a dead thread");
548 Log.w(TAG, e.getMessage(), e);
549 msg.recycle();
550 return false;
551 }
552
553 msg.markInUse();//標記使用
554 msg.when = when;
555 Message p = mMessages;
556 boolean needWake;
557 if (p == null || when == 0 || when < p.when) {//鏈表爲空或者時間小於鏈表第一個消息,將消息插入鏈表第一個
558 // New head, wake up the event queue if blocked.
559 msg.next = p;
560 mMessages = msg;
561 needWake = mBlocked;
562 } else {//將消息按when來插入合適的位置
563 // Inserted within the middle of the queue. Usually we don't have to wake
564 // up the event queue unless there is a barrier at the head of the queue
565 // and the message is the earliest asynchronous message in the queue.
566 needWake = mBlocked && p.target == null && msg.isAsynchronous();
567 Message prev;
568 for (;;) {
569 prev = p;
570 p = p.next;
571 if (p == null || when < p.when) {
572 break;
573 }
574 if (needWake && p.isAsynchronous()) {
575 needWake = false;
576 }
577 }
578 msg.next = p; // invariant: p == prev.next
579 prev.next = msg;
580 }
581
582 // We can assume mPtr != 0 because mQuitting is false.
583 if (needWake) {//mBlocked在next方法裏面設值,其實就是當前線程處理等待的時候,才需要喚醒
584 nativeWake(mPtr);
585 }
586 }
587 return true;
588 }
邏輯很簡單,如果當前MessageQueue的mMessages是否有過鏈表,如果沒有,就把鏈表設置爲參數msg。如果有,就根據msg的觸發時間when插入mMessages鏈表。這裏有個needWake,根據mBlocked等條件來判斷是否需要調用nativeWake。到這裏,我們看到了native調用。其實MessageQueue實例化的時候就調用了nativeInit,且後再提。先來看看這裏的mBlocked變量被改的方法next()(next方法是被Looper.loop()方法循環裏面調用的)。
Tips:
- next方法是被Looper.loop()方法循環裏面調用的。
- Message有個成員變量 Message next,然後再加上MessageQueue的mMessages prev,就是一個鏈表了。
frameworks/base/core/java/android/os/MessageQueue.java
310 Message next() {
311 // Return here if the message loop has already quit and been disposed.
312 // This can happen if the application tries to restart a looper after quit
313 // which is not supported.
314 final long ptr = mPtr;//mPtr是持有的native層的NativeMessageQueue對象
315 if (ptr == 0) {
316 return null;
317 }
318
319 int pendingIdleHandlerCount = -1; // -1 only during first iteration
320 int nextPollTimeoutMillis = 0;
321 for (;;) {
322 if (nextPollTimeoutMillis != 0) {
323 Binder.flushPendingCommands();
324 }
325
//實際上是調用Looper.cpp裏面的pollOnce,基於linux的epoll,後續詳細分析
326 nativePollOnce(ptr, nextPollTimeoutMillis);
327
328 synchronized (this) {
329 // Try to retrieve the next message. Return if found.
330 final long now = SystemClock.uptimeMillis();
331 Message prevMsg = null;
332 Message msg = mMessages;
333 if (msg != null && msg.target == null) {
334 // Stalled by a barrier. Find the next asynchronous message in the queue.
335 do {//過濾掉異步處理msg
336 prevMsg = msg;
337 msg = msg.next;
338 } while (msg != null && !msg.isAsynchronous());
339 }
340 if (msg != null) {
341 if (now < msg.when) {//當前消息時間嗨沒到,設置nextPollTimeoutMillis,然後通知native去pollOnce等待
342 // Next message is not ready. Set a timeout to wake up when it is ready.
343 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
344 } else {//時間已經到,返回消息給Looper進行分發處理
345 // Got a message.
346 mBlocked = false;
347 if (prevMsg != null) {
348 prevMsg.next = msg.next;
349 } else {
350 mMessages = msg.next;
351 }
352 msg.next = null;
353 if (DEBUG) Log.v(TAG, "Returning message: " + msg);
354 msg.markInUse();
355 return msg;
356 }
357 } else {
358 // No more messages.
359 nextPollTimeoutMillis = -1;
360 }
361
362 // Process the quit message now that all pending messages have been handled.
363 if (mQuitting) {
364 dispose();
365 return null;
366 }
367
//下面的用於mIdleHandlers狀態的一些處理,比如GC
368 // If first time idle, then get the number of idlers to run.
369 // Idle handles only run if the queue is empty or if the first message
370 // in the queue (possibly a barrier) is due to be handled in the future.
371 if (pendingIdleHandlerCount < 0
372 && (mMessages == null || now < mMessages.when)) {
373 pendingIdleHandlerCount = mIdleHandlers.size();
374 }
375 if (pendingIdleHandlerCount <= 0) {
376 // No idle handlers to run. Loop and wait some more.
377 mBlocked = true;//標記下次enqueueMessage的時候,需要喚醒
378 continue;
379 }
380
381 if (mPendingIdleHandlers == null) {
382 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
383 }
384 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
385 }
386
387 // Run the idle handlers.
388 // We only ever reach this code block during the first iteration.
389 for (int i = 0; i < pendingIdleHandlerCount; i++) {
390 final IdleHandler idler = mPendingIdleHandlers[i];
391 mPendingIdleHandlers[i] = null; // release the reference to the handler
392
393 boolean keep = false;
394 try {
395 keep = idler.queueIdle();
396 } catch (Throwable t) {
397 Log.wtf(TAG, "IdleHandler threw exception", t);
398 }
399
400 if (!keep) {
401 synchronized (this) {
402 mIdleHandlers.remove(idler);
403 }
404 }
405 }
406
407 // Reset the idle handler count to 0 so we do not run them again.
408 pendingIdleHandlerCount = 0;
409
410 // While calling an idle handler, a new message could have been delivered
411 // so go back and look again for a pending message without waiting.
412 nextPollTimeoutMillis = 0;
413 }
414 }
最後來看下Looper調用MessageQueue的next方法的地方,Looper.loop(),去掉了一些計算分發時間代碼。
frameworks/base/core/java/android/os/Looper.java
/**
134 * Run the message queue in this thread. Be sure to call
135 * {@link #quit()} to end the loop.
136 */
137 public static void loop() {
138 final Looper me = myLooper();
139 if (me == null) {
140 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
141 }
142 final MessageQueue queue = me.mQueue;//取出鏈表
143
144 // Make sure the identity of this thread is that of the local process,
145 // and keep track of what that identity token actually is.
146 Binder.clearCallingIdentity();
147 final long ident = Binder.clearCallingIdentity();
148
...
158
159 for (;;) {
//這裏取出消息,如果消息時間未到,會被block
160 Message msg = queue.next(); // might block
161 if (msg == null) {
162 // No message indicates that the message queue is quitting.
163 return;
164 }
165
...
//分發處理消息,調用msg的handler處理消息,最終回調callback/override的handleMessage(msg),對應到test代碼就是07行的handleMessage
192 try {
193 msg.target.dispatchMessage(msg);
194 dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
195 } finally {
196 if (traceTag != 0) {
197 Trace.traceEnd(traceTag);
198 }
199 }
...
222 // Make sure that during the course of dispatching the
223 // identity of the thread wasn't corrupted.
224 final long newIdent = Binder.clearCallingIdentity();
225 if (ident != newIdent) {
226 Log.wtf(TAG, "Thread identity changed from 0x"
227 + Long.toHexString(ident) + " to 0x"
228 + Long.toHexString(newIdent) + " while dispatching to "
229 + msg.target.getClass().getName() + " "
230 + msg.callback + " what=" + msg.what);
231 }
232
233 msg.recycleUnchecked();//消息recycle,是否真正的GC回收了,前面有講述
234 }
235 }
到這裏,消息分發處理的邏輯都走完。
但是中間有略去native層的業務,線程阻塞、線程喚醒、native層的MessageQueue等,請見下回講解。