Android訊息機制中Message常用的幾種監控方式

語言: CN / TW / HK

highlight: vs theme: devui-blue


持續創作,加速成長!這是我參與「掘金日新計劃 · 10 月更文挑戰」的第4天,點選檢視活動詳情

本篇文章主要是講解Android訊息機制中Message執行的幾種監控方式: 1. Printer監聽Message執行的起始時機

  1. Observer監聽Message執行的起始時機並將Message作為引數傳入

  2. dump方式列印訊息佇列中Message快照

上面幾種方式各有其優缺點及適用場景,下面我們一一進行分析(其中,Android SDK32中Looper的原始碼發生了一些變化,不過不影響閱讀)。

Printer方式

對應Looper原始碼中的:

image.png

image.png

我們直接深入到Looper的核心方法loopOnce()(基於SDK32的原始碼)進行分析: java private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { Message msg = me.mQueue.next(); // might block ... final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid); try { msg.target.dispatchMessage(msg); ... } ... if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } ... msg.recycleUnchecked(); return true; }

其中msg.target.dispatchMessage()就是我們訊息分發執行的地方,而在這個執行前後都會呼叫Printer.println()方法。

所以如果我們能夠將這個Printer物件替換成我們自定義的,不就可以監聽Message執行和結束的時機,所幸,Looper也確實提供了一個方法setMessageLogging()支援外部自定義Printer傳入:

java public void setMessageLogging(@Nullable Printer printer) { mLogging = printer; }

這個有什麼用呢,比如可以用來監聽耗時的Message,從而定位到業務程式碼中卡頓的程式碼位置進行優化,ANRWatchDog據我所知就使用了這樣的原理。

Observer方式

這個定位到Looper原始碼中就是:

image.png

image.png

可以看到這個介面提供的方法引數更加豐富,我們看下它在原始碼中的呼叫位置(精簡後的程式碼如下):

private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { Message msg = me.mQueue.next(); // might block final Observer observer = sObserver; Object token = null; if (observer != null) { token = observer.messageDispatchStarting(); } try { msg.target.dispatchMessage(msg); if (observer != null) { observer.messageDispatched(token, msg); } } catch (Exception exception) { if (observer != null) { observer.dispatchingThrewException(token, msg, exception); } throw exception; } }

和上面的Printer呼叫有點相似,也是在訊息執行前、訊息執行後呼叫,其中執行後分為兩種:

  1. 正常執行後呼叫messageDispatched()

  2. 異常執行後呼叫dispatchingThrewException()

下面我們簡單的介紹Observer這三個介面方法:

messageDispatchStarting()Message執行之前進行呼叫,並且可以返回一個標識來標識這條Message訊息,這樣當訊息正常執行結束後,呼叫messageDispatched()方法傳入這個標識和當前分發的Message,我們就可以建立這個標識和Message之間的對映關係;出現異常的時候就會呼叫dispatchingThrewException()方法,除了傳入標識和分發的Message外,還會傳入捕捉到的異常。

不過很遺憾的是,Observer是個被@Hide標記的,不允許開發者進行呼叫,如果大家真要使用,可以參考這篇文章:監控Android Looper Message排程的另一種姿勢

dump方式

這個可以列印當前訊息佇列中每條訊息的快照資訊,可以根據需要進行呼叫:

  1. Looper.dump():

java public void dump(@NonNull Printer pw, @NonNull String prefix) { pw.println(prefix + toString()); mQueue.dump(pw, prefix + " ", null); }

  1. MessageQueue.dump()

java void dump(Printer pw, String prefix, Handler h) { synchronized (this) { long now = SystemClock.uptimeMillis(); int n = 0; for (Message msg = mMessages; msg != null; msg = msg.next) { if (h == null || h == msg.target) { pw.println(prefix + "Message " + n + ": " + msg.toString(now)); } n++; } pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() + ", quitting=" + mQuitting + ")"); } }

很直觀的可以看到,當呼叫dump()方法時會傳入一個Printer物件例項,就會遍歷訊息佇列mMessages,通過傳入的Printer列印每條訊息的內容。

其中Message重寫了toString()方法:

image.png

大家可以根據需要自行使用。

參考

監控Android Looper Message排程的另一種姿勢