Android Handler揭祕(二)

簡述:

繼續Android Handler揭祕(一),這裏來繼續分析MessageQueue。

相關代碼:

  1. frameworks/base/core/java/android/os/MessageQueue.java
  2. frameworks/base/core/jni/android_os_MessageQueue.cpp
  3. 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:

  1. next方法是被Looper.loop()方法循環裏面調用的。
  2. 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等,請見下回講解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章