Android平臺(tái)上,主要用到的通信機(jī)制有兩種:Handler和Binder,前者用于進(jìn)程內(nèi)部的通信,后者主要用于跨進(jìn)程通信。
目錄
本文基于原生 Android9.0源碼來解析 Android 消息機(jī)制:
frameworks/base/core/java/android/os/Handler.java
frameworks/base/core/java/android/os/Looper.java
frameworks/base/core/java/android/os/MessageQueue.java
frameworks/base/core/java/android/os/Message.java
frameworks/base/core/java/android/app/ActivityThread.java
1. 概述
我們知道在Android的主線程中不能進(jìn)行耗時(shí)操作,例如網(wǎng)絡(luò)訪問、數(shù)據(jù)處理等,因?yàn)橐坏┲骶€程的任務(wù)處理時(shí)間超過系統(tǒng)規(guī)定的限制就會(huì)出現(xiàn)應(yīng)用不響應(yīng)的情況。但在實(shí)際工作中,處理耗時(shí)任務(wù)是不可避免的,而且經(jīng)常需要在處理完耗時(shí)任務(wù)后更新某些UI控件,以顯示處理結(jié)果。在這種場(chǎng)景下,最常用方案就是在新線程中進(jìn)行耗時(shí)操作,處理完成后通知主線程進(jìn)行相關(guān)UI的更新,這時(shí)就需要使用到Android消息機(jī)制了。
到底什么是消息機(jī)制呢?簡(jiǎn)單來說,Android消息機(jī)制是一套以“消息”為中介來實(shí)現(xiàn)線程之間的任務(wù)切換或同一線程中任務(wù)的按需執(zhí)行的機(jī)制,其中涉及到消息的發(fā)送、存儲(chǔ)消息、消息循環(huán)以及消息的分發(fā)和處理。
本文將先通過一個(gè)簡(jiǎn)單的示例演示如何使用Android消息機(jī)制,再通過分析源碼來進(jìn)一步了解消息機(jī)制的內(nèi)部實(shí)現(xiàn)方式,最后會(huì)講解一些使用Android消息機(jī)制的注意點(diǎn)。
2. 初見 Android 消息機(jī)制
先用一個(gè)簡(jiǎn)單示例來展示下Android消息機(jī)制在實(shí)際工作中如何使用,就直接利用前面提到的場(chǎng)景,即子線程處理耗時(shí)任務(wù)并在任務(wù)處理完畢后通知主線程進(jìn)行UI的更新,示例代碼如下:


小菜在示例代碼里通過序號(hào)標(biāo)注了邏輯流程,即先開啟子線程并在線程內(nèi)部處理任務(wù),任務(wù)處理完成后通過Handler向主線程發(fā)送消息,最后在主線程中處理消息并更新UI。
看起來Android消息機(jī)制很簡(jiǎn)單嘛,只要利用Handler發(fā)送消息并處理其中的消息就可以了嘛。真的這么簡(jiǎn)單嗎?當(dāng)然不是!前面提到過在消息機(jī)制中涉及到幾個(gè)關(guān)鍵點(diǎn):發(fā)送消息、存儲(chǔ)消息、消息循環(huán)和分發(fā)處理消息,在這個(gè)示例中我們只看到了發(fā)送消息和處理消息,并沒有看到存儲(chǔ)消息和消息循環(huán)。
這是因?yàn)檫@個(gè)例子中的Handler使用的消息是發(fā)送和存儲(chǔ)在主線程中的消息隊(duì)列中,這個(gè)消息隊(duì)列的創(chuàng)建和循環(huán)都是在主線程創(chuàng)建的時(shí)候系統(tǒng)自動(dòng)進(jìn)行的,對(duì)我們是透明的,不利于理解消息機(jī)制的整體流程。
現(xiàn)在給出一個(gè)更為通用的示例,從這個(gè)例子中可以清楚地看到消息隊(duì)列的創(chuàng)建和消息循環(huán)的開啟:
class LooperThread extends Thread {
? ? public Handler mHandler;
? ? public void run() {
? ? ? ? Looper.prepare(); ?? // 初始化 Looper 對(duì)象,其內(nèi)部會(huì)創(chuàng)建消息隊(duì)列。
? ? ? ? mHandler = new Handler() {
? ? ? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? // 處理消息隊(duì)列中的消息。
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? Looper.loop(); ?? // 開啟消息循環(huán),會(huì)從消息隊(duì)列中取出消息,沒有消息時(shí)等待新消息的到來。
? ? }
}
綜合這兩個(gè)示例,我們了解了Android消息機(jī)制的使用方法,也看到了發(fā)送消息、創(chuàng)建消息隊(duì)列、開啟消息循環(huán)以及處理消息的過程,下面給出一個(gè)更直觀的“消息傳遞流程圖”:
通過流程圖可以看到整個(gè)消息傳遞過程,也可以看到在不同的階段涉及的類:
1. 消息發(fā)送:通過Handler向關(guān)聯(lián)的MessageQueue發(fā)送消息;
2. 消息存儲(chǔ): 把發(fā)送的消息以Message的形式存儲(chǔ)在MessageQueue中;
3. 消息循環(huán):通過Looper不停地從MessageQueue中獲取消息,隊(duì)列中沒有消息時(shí)就阻塞等待新消息;
4. 消息分發(fā)和處理:Looper獲取消息后分發(fā)給Handler進(jìn)行處理。
3. 理解 Android 消息機(jī)制
前面提到消息傳遞流程主要分為“發(fā)送消息”、“存儲(chǔ)消息”、“消息循環(huán)”和“消息分發(fā)和處理”幾個(gè)不同階段,小菜本打算按照這個(gè)流程來分別講解每個(gè)階段,但是在具體行文的時(shí)候發(fā)現(xiàn)每個(gè)階段并不是完全分割開來的,比如在講“發(fā)送消息”之前要先了解“消息的存儲(chǔ)結(jié)構(gòu)”和“消息循環(huán)的開啟”,而“消息的分發(fā)”又是屬于“消息循環(huán)”的功能。
正是由于這幾個(gè)階段之間的相互關(guān)系,導(dǎo)致沒有辦法嚴(yán)格按照消息傳遞的順序講解Android消息機(jī)制。思慮再三,小菜決定通過上面講解的Android消息機(jī)制通用示例來一步步解析其背后的邏輯流程。
再來看下通用示例:
class LooperThread extends Thread {
? ? public Handler mHandler;
? ? public void run() {
? ? ? ? // 1. 初始化 Looper 對(duì)象,其內(nèi)部會(huì)創(chuàng)建消息隊(duì)列。
? ? ? ? Looper.prepare();
? ? ? ? mHandler = new Handler() {
? ? ? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? // 4. 處理消息隊(duì)列中的消息。
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? // 2. 開啟消息循環(huán),會(huì)從消息隊(duì)列中取出消息,沒有消息時(shí)阻塞等待新消息的到來。
? ? ? ? Looper.loop();
? ? }
? ? // 3. 發(fā)送消息
? ? mHandler.sendEmptyMessage(0);
}
在示例代碼中用不同的序號(hào)標(biāo)注了“消息傳遞機(jī)制”的各個(gè)關(guān)鍵點(diǎn),以下的內(nèi)容也都是根據(jù)這些關(guān)鍵節(jié)點(diǎn)進(jìn)行講解的。
3.1 消息載體
“消息”是Android消息機(jī)制中信息的載體,它包含了在整個(gè)消息傳遞過程中想要傳送的數(shù)據(jù),要理解“消息機(jī)制”就要先了解這個(gè)消息載體類Message:
/**
* Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}.? This object contains two extra int fields and an
* extra object field that allow you to not do allocations in many cases.
*
* <p class="note">While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.</p>
*/
public final class Message implements Parcelable { ... }
Android框架中對(duì)消息載體Message的聲明雖然簡(jiǎn)短,卻傳達(dá)了最核心最重要的兩點(diǎn)信息:
Message的作用:Message是包含了描述信息和數(shù)據(jù)對(duì)象并且在消息機(jī)制中發(fā)送給Handler的對(duì)象,其中的數(shù)據(jù)對(duì)象主要包括兩個(gè)整型域和一個(gè)對(duì)象域,通過這些域可以傳遞信息。整型的代價(jià)是最小的,所以盡量使用整型域傳遞信息。
/**
* User-defined message code so that the recipient can identify
* what this message is about. Each {@link Handler} has its own name-space
* for message codes, so you do not need to worry about yours conflicting
* with other handlers.
*/
public int what;
/**
* arg1 and arg2 are lower-cost alternatives to using
* {@link #setData(Bundle) setData()} if you only need to store a
* few integer values.
*/
public int arg1;
public int arg2;
/**
* An arbitrary object to send to the recipient.? When using
* {@link Messenger} to send the message across processes this can only
* be non-null if it contains a Parcelable of a framework class (not one
* implemented by the application).? For other data transfer use
* {@link #setData}.
*
* <p>Note that Parcelable objects here are not supported prior to
* the {@link android.os.Build.VERSION_CODES#FROYO} release.
*/
public Object obj;
Message的創(chuàng)建方式:雖然Message有公有構(gòu)造函數(shù),但是建議使用其提供的obtain系列函數(shù)來獲取Message對(duì)象,這種創(chuàng)建方式會(huì)重復(fù)利用緩存池中的對(duì)象而不是直接創(chuàng)建新的對(duì)象,從而避免在內(nèi)存中創(chuàng)建太多對(duì)象,避免可能的性能問題。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
? ? synchronized (sPoolSync) {
? ? ? ? // 緩存池中存在可用對(duì)象時(shí)去緩存池獲取 Message 對(duì)象。
? ? ? ? if (sPool != null) {
? ? ? ? ? ? // 獲取緩存中的對(duì)象,并把緩存池指針后移。
? ? ? ? ? ? Message m = sPool;
? ? ? ? ? ? sPool = m.next;
? ? ? ? ? ? m.next = null;
? ? ? ? ? ? // 清除標(biāo)志位
? ? ? ? ? ? m.flags = 0; // clear in-use flag
? ? ? ? ? ? // 更新當(dāng)前緩存池大小
? ? ? ? ? ? sPoolSize--;
? ? ? ? ? ? return m;
? ? ? ? }
? ? }
? ? // 緩存池中沒有可用對(duì)象時(shí)直接創(chuàng)建一個(gè)新的 Message 對(duì)象。
? ? return new Message();
}
Message中有一系列obtain函數(shù)用以在不同場(chǎng)景中獲取對(duì)象,但這個(gè)是最核心的,其他函數(shù)都會(huì)在其內(nèi)部調(diào)用它,有興趣的同學(xué)可以自行查看源碼,考慮到篇幅問題,這里就不再一一列舉說明了。
看到這里,相信大家都會(huì)有一個(gè)疑問:obtain函數(shù)是從緩存池中獲取Message對(duì)象,那緩存池中的對(duì)象是什么時(shí)候被添加進(jìn)去的呢?既然緩存池中的對(duì)象都是一些可以被重復(fù)使用的對(duì)象,很明顯是在Message對(duì)象不再被需要的時(shí)候,即從MessageQueue中取出并分發(fā)給Handler的時(shí)候,被添加到緩存中的,使用的是recycleUnchecked函數(shù):
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
? ? // 設(shè)置標(biāo)志位為“使用中”,在從緩存中取出時(shí)會(huì)清除這個(gè)標(biāo)志位。
? ? flags = FLAG_IN_USE;
? ? // Message 對(duì)象中的信息都不再有意義,在放入緩存池前直接清空。
? ? what = 0;
? ? arg1 = 0;
? ? arg2 = 0;
? ? obj = null;
? ? replyTo = null;
? ? sendingUid = -1;
? ? when = 0;
? ? target = null;
? ? callback = null;
? ? data = null;
? ? synchronized (sPoolSync) {
? ? ? ? // 緩存池中只緩存一定數(shù)量的 Message 對(duì)象,默認(rèn)是 50 個(gè)。
? ? ? ? if (sPoolSize < MAX_POOL_SIZE) {
? ? ? ? ? ? // 把對(duì)象放在緩存池的鏈表首部。
? ? ? ? ? ? next = sPool;
? ? ? ? ? ? sPool = this;
? ? ? ? ? ? // 及時(shí)更新緩存池大小。
? ? ? ? ? ? sPoolSize++;
? ? ? ? }
? ? }
}
3.2 創(chuàng)建消息隊(duì)列
消息隊(duì)列的創(chuàng)建對(duì)消息傳遞至關(guān)重要,它決定了消息在傳遞過程中的存取方式。但是線程在默認(rèn)情況下是沒有消息隊(duì)列的,也無法在其內(nèi)部進(jìn)行消息循環(huán)。如果想為線程開啟消息循環(huán)就需要使用到Looper類,它可以為關(guān)聯(lián)的線程創(chuàng)建消息隊(duì)列并開啟消息循環(huán),創(chuàng)建消息隊(duì)列的方式是調(diào)用prepare()接口:
/**
? * Class used to run a message loop for a thread.? Threads by default do
? * not have a message loop associated with them; to create one, call
? * {@link #prepare} in the thread that is to run the loop, and then
? * {@link #loop} to have it process messages until the loop is stopped.
? */
public final class Looper {
? ? // 省略無關(guān)代碼
? ? // sThreadLocal.get() will return null unless you've called prepare().
? ? static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
? ? // 內(nèi)部的消息隊(duì)列和關(guān)聯(lián)的線程
? ? final MessageQueue mQueue;
? ? final Thread mThread;
? ? // 省略無關(guān)代碼
? ? /** Initialize the current thread as a looper.
? ? ? * This gives you a chance to create handlers that then reference
? ? ? * this looper, before actually starting the loop. Be sure to call
? ? ? * {@link #loop()} after calling this method, and end it by calling
? ? ? * {@link #quit()}.
? ? ? */
? ? public static void prepare() {
? ? ? ? // 創(chuàng)建可退出的消息循環(huán),主線程的消息循環(huán)是不可退出的。
? ? ? ? prepare(true);
? ? }
? ? private static void prepare(boolean quitAllowed) {
? ? ? ? // 如果當(dāng)前線程已經(jīng)有了 Looper 對(duì)象就直接拋出異常,
? ? ? ? // 因?yàn)橐粋€(gè)線程只能有一個(gè)消息隊(duì)列。
? ? ? ? if (sThreadLocal.get() != null) {
? ? ? ? ? ? throw new RuntimeException("Only one Looper may be created per thread");
? ? ? ? }
? ? ? ? // 創(chuàng)建 Looper 對(duì)象并和線程關(guān)聯(lián)。
? ? ? ? sThreadLocal.set(new Looper(quitAllowed));
? ? }
? ? // 私有構(gòu)造函數(shù),創(chuàng)建消息隊(duì)列并獲取當(dāng)前線程對(duì)象。
? ? private Looper(boolean quitAllowed) {
? ? ? ? mQueue = new MessageQueue(quitAllowed);
? ? ? ? mThread = Thread.currentThread();
? ? }
可以看到Looper.prepare()只是在內(nèi)部創(chuàng)建了一個(gè)MessageQueue對(duì)象并和當(dāng)前線程關(guān)聯(lián)起來,同時(shí)還保證了每個(gè)線程只能有一個(gè)消息隊(duì)列。
很顯然MessageQueue就是用來存儲(chǔ)消息對(duì)象的結(jié)構(gòu)了,看下它的聲明:
/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}.? Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
public final class MessageQueue { ... }
MessageQueue是一個(gè)持有消息對(duì)象列表的類,而這些消息對(duì)象通過和Looper關(guān)聯(lián)的Handler添加并最終由Looper進(jìn)行分發(fā),其中有個(gè)關(guān)鍵信息需要引起我們的格外關(guān)注:list of messages,這是不是告訴我們雖然這個(gè)類的名字是queue但是其內(nèi)部并不是隊(duì)列而是列表呢?確實(shí)如此,MessageQueue的內(nèi)部是使用單向鏈表的方法進(jìn)行存取的,這點(diǎn)在后面解析Message的存取過程中會(huì)看到,在這里就不詳細(xì)講述了。
3.3 開啟消息循環(huán)
“消息隊(duì)列”創(chuàng)建完成了,是不是就可以直接向其中添加消息對(duì)象了呢?還不到時(shí)候,還需要先開啟消息循環(huán),來監(jiān)聽消息隊(duì)列的情況,這時(shí)需要使用Looper.loop()接口:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
? ? // 獲取當(dāng)前線程的 Looper 對(duì)象,獲取失敗時(shí)拋出異常。
? ? final Looper me = myLooper();
? ? if (me == null) {
? ? ? ? throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
? ? }
? ? // 獲取當(dāng)前線程的消息隊(duì)列。
? ? final MessageQueue queue = me.mQueue;
? ? // 省略無關(guān)代碼
? ? // 開啟一個(gè)無限循環(huán)來監(jiān)聽消息隊(duì)列的情況
? ? for (;;) {
? ? ? ? // 獲取消息隊(duì)列中的消息對(duì)象,如果沒有消息對(duì)象就阻塞等待。
? ? ? ? Message msg = queue.next(); // might block
? ? ? ? if (msg == null) {
? ? ? ? ? ? // 消息隊(duì)列正在退出時(shí)就終止監(jiān)聽并退出循環(huán)
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? // 省略無關(guān)代碼
? ? ? ? try {
? ? ? ? ? ? // 分發(fā)消息,把消息發(fā)送合適的處理對(duì)象。
? ? ? ? ? ? msg.target.dispatchMessage(msg);
? ? ? ? ? ? dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
? ? ? ? } finally {
? ? ? ? ? ? if (traceTag != 0) {
? ? ? ? ? ? ? ? Trace.traceEnd(traceTag);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 省略無關(guān)代碼
? ? ? ? // 回收消息對(duì)象,放入消息緩存池中以待后續(xù)復(fù)用。
? ? ? ? msg.recycleUnchecked();
? ? }
}
這段代碼本身比較復(fù)雜,小菜省略了其中和核心邏輯無關(guān)的部分代碼,以方便大家閱讀和理解,其核心邏輯就是利用一個(gè)“無限循環(huán)”來監(jiān)聽消息隊(duì)列,當(dāng)發(fā)現(xiàn)有可用消息就取出并分發(fā)處理,如果沒有就一直等待。
3.4 發(fā)送和存儲(chǔ)消息
“消息隊(duì)列”已經(jīng)創(chuàng)建完成,“消息循環(huán)”也已經(jīng)開啟,終于可用發(fā)送消息了。
要發(fā)送消息,就要使用到Handler類了,其中的send和post系列方法都可以進(jìn)行“消息的發(fā)送”,核心方法都是一樣的,在這里就以post方法來講解下發(fā)送消息的過程:
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*?
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
*? ? ? ? message queue.? Returns false on failure, usually because the
*? ? ? ? looper processing the message queue is exiting.
*/
public final boolean post(Runnable r) {
? ? return? sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
? ? // 把 Runnable 對(duì)象封裝成 Message 并設(shè)置 callback,
? ? // 這個(gè) callback 會(huì)在后面消息的分發(fā)處理中起到作用。
? ? Message m = Message.obtain();
? ? m.callback = r;
? ? return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
? ? if (delayMillis < 0) {
? ? ? ? delayMillis = 0;
? ? }
? ? // 把延遲時(shí)間轉(zhuǎn)換為絕對(duì)時(shí)間,方便后續(xù)執(zhí)行。
? ? return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
? ? // 消息隊(duì)列,即通過 Looper.prepare() 創(chuàng)建的消息隊(duì)列。
? ? MessageQueue queue = mQueue;
? ? if (queue == null) {
? ? ? ? RuntimeException e = new RuntimeException(
? ? ? ? ? ? ? ? this + " sendMessageAtTime() called with no mQueue");
? ? ? ? Log.w("Looper", e.getMessage(), e);
? ? ? ? return false;
? ? }
? ? // 把消息添加到消息隊(duì)列
? ? return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
? ? // 設(shè)置消息隊(duì)列的目標(biāo),用于后續(xù)的消息分發(fā)過程。
? ? msg.target = this;
? ? if (mAsynchronous) {
? ? ? ? msg.setAsynchronous(true);
? ? }
? ? // 消息對(duì)象入隊(duì)
? ? return queue.enqueueMessage(msg, uptimeMillis);
}
通過一系列的調(diào)用過程,Handler最終會(huì)通過MessageQueue.enqueueMessage()把消息存儲(chǔ)到消息隊(duì)列中,MessageQueue內(nèi)部又是如何存儲(chǔ)這個(gè)發(fā)送過來的消息對(duì)象的呢?
boolean enqueueMessage(Message msg, long when) {
? ? // 消息對(duì)象的目標(biāo)是 null 時(shí)直接拋出異常,因?yàn)檫@意味這個(gè)消息無法進(jìn)行分發(fā)處理,
? ? // 是不合法的消息對(duì)象。
? ? if (msg.target == null) {
? ? ? ? throw new IllegalArgumentException("Message must have a target.");
? ? }
? ? // 消息正在使用時(shí)拋出異常,消息不能并發(fā)使用。
? ? if (msg.isInUse()) {
? ? ? ? throw new IllegalStateException(msg + " This message is already in use.");
? ? }
? ? synchronized (this) {
? ? ? ? // 正在退出消息循環(huán)時(shí),回收消息對(duì)象。
? ? ? ? if (mQuitting) {
? ? ? ? ? ? IllegalStateException e = new IllegalStateException(
? ? ? ? ? ? ? ? ? ? msg.target + " sending message to a Handler on a dead thread");
? ? ? ? ? ? Log.w(TAG, e.getMessage(), e);
? ? ? ? ? ? msg.recycle();
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? msg.markInUse();
? ? ? ? msg.when = when;
? ? ? ? Message p = mMessages;
? ? ? ? boolean needWake;
? ? ? ? // 把消息對(duì)象添加到消息隊(duì)列的合適位置
? ? ? ? if (p == null || when == 0 || when < p.when) {
? ? ? ? ? ? // New head, wake up the event queue if blocked.
? ? ? ? ? ? // 消息隊(duì)列為空或者當(dāng)前消息對(duì)象的時(shí)間最近,直接放在鏈表首部。
? ? ? ? ? ? msg.next = p;
? ? ? ? ? ? // 更新鏈表指針
? ? ? ? ? ? mMessages = msg;
? ? ? ? ? ? // 如果原來消息循環(huán)處于阻塞狀態(tài)就重新喚醒
? ? ? ? ? ? needWake = mBlocked;
? ? ? ? } else {
? ? ? ? ? ? // Inserted within the middle of the queue.? Usually we don't have to wake
? ? ? ? ? ? // up the event queue unless there is a barrier at the head of the queue
? ? ? ? ? ? // and the message is the earliest asynchronous message in the queue.
? ? ? ? ? ? needWake = mBlocked && p.target == null && msg.isAsynchronous();
? ? ? ? ? ? Message prev;
? ? ? ? ? ? // 根據(jù)消息對(duì)象中的時(shí)間信息尋找合適的插入位置
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? prev = p;
? ? ? ? ? ? ? ? p = p.next;
? ? ? ? ? ? ? ? if (p == null || when < p.when) {
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (needWake && p.isAsynchronous()) {
? ? ? ? ? ? ? ? ? ? needWake = false;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? // 找到合適位置后插入鏈表
? ? ? ? ? ? msg.next = p; // invariant: p == prev.next
? ? ? ? ? ? prev.next = msg;
? ? ? ? }
? ? ? ? // We can assume mPtr != 0 because mQuitting is false.
? ? ? ? // 喚醒等待,這時(shí)消息循環(huán)可以繼續(xù)獲取消息了,之前有可能處于阻塞等待狀態(tài)。
? ? ? ? if (needWake) {
? ? ? ? ? ? nativeWake(mPtr);
? ? ? ? }
? ? }
? ? return true;
}
3.5 消息分發(fā)處理
當(dāng)消息隊(duì)列中有新的消息并且消息循環(huán)被喚醒后,消息隊(duì)列中的消息就可以被取出并分發(fā)給合適的處理者了,這點(diǎn)可以在“開啟消息循環(huán)”一節(jié)中看到,利用的是msg.target.dispatchMessage(msg),而target就是Handler對(duì)象,直接看具體的分發(fā)過程:
public void dispatchMessage(Message msg) {
? ? // Message 對(duì)象是從 Runnable 封裝形成的時(shí)候,callback 不為空。
? ? if (msg.callback != null) {
? ? ? ? handleCallback(msg);
? ? } else {
? ? ? ? // mCallback 是在 Handler 的構(gòu)造函數(shù)中設(shè)置的,也可以不設(shè)置。
? ? ? ? if (mCallback != null) {
? ? ? ? ? ? // 調(diào)用 Handler 的 callback 處理消息
? ? ? ? ? ? if (mCallback.handleMessage(msg)) {
? ? ? ? ? ? ? ? // 可以攔截消息,之后 Handler.handleMessage 將無法繼續(xù)處理這個(gè)消息。
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 調(diào)用 Handler 的 handleMessage 處理消息,子類會(huì)實(shí)現(xiàn)這個(gè)方法。
? ? ? ? handleMessage(msg);
? ? }
}
private static void handleCallback(Message message) {
? ? // Message 中的 callback 是 Runnable,直接執(zhí)行 Runnable.run()。
? ? message.callback.run();
}
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
? ? /**
? ? * @param msg A {@link android.os.Message Message} object
? ? * @return True if no further handling is desired
? ? */
? ? // Handler 的回調(diào)方法,通過返回值可以進(jìn)行消息攔截。
? ? public boolean handleMessage(Message msg);
}
/**
* Subclasses must implement this to receive messages.
*/
// Handler 的處理消息回調(diào),子類需要實(shí)現(xiàn)。
public void handleMessage(Message msg) {
}
消息的分發(fā)是有一定優(yōu)先順序的:
首先會(huì)考慮交給Message.callback來處理,如果是通過post系列函數(shù)發(fā)送的消息會(huì)走到這里進(jìn)行處理,而通過send系列函數(shù)發(fā)送的消息默認(rèn)是沒有這個(gè)回調(diào)接口的;
如果Message.callback不存在就考慮交給Handler.callback來處理,在處理過程中可以通過返回值攔截消息;
如果Handler.callback不存在或者存在但是在處理消息過程中沒有進(jìn)行攔截,就會(huì)交給Handler.handleMessage來處理,這個(gè)接口需要子類實(shí)現(xiàn),也是在實(shí)際工作中最常用的處理消息的地方。
到這里,消息的傳遞過程就基本講完了,大家可以結(jié)合之前的流程圖仔細(xì)揣摩,相信可以對(duì)Android消息機(jī)制有更深刻的理解。
4. 延伸知識(shí)點(diǎn)
4.1 主線程消息循環(huán)的創(chuàng)建
前面講到一個(gè)線程默認(rèn)是沒有消息隊(duì)列的,也無法在其內(nèi)部開啟消息循環(huán),但是我們?cè)趯?shí)際工作中經(jīng)常會(huì)直接在主線程中使用Handler來進(jìn)行消息的發(fā)送和處理,并且運(yùn)行正常,這是因?yàn)橹骶€程在啟動(dòng)的時(shí)候就已經(jīng)創(chuàng)建了消息隊(duì)列并開啟了消息循環(huán),只是這個(gè)過程是透明的,我們沒有感知到。
了解Activity啟動(dòng)過程的同學(xué)應(yīng)該已經(jīng)想到了這個(gè)創(chuàng)建過程是在哪里了,沒錯(cuò),就是在ActivityThread,不了解啟動(dòng)過程的同學(xué)也不要擔(dān)心,后續(xù)我會(huì)講解具體的啟動(dòng)過程。在這里,大家只要簡(jiǎn)單地把ActivityThread當(dāng)做Activity的啟動(dòng)入口即可,直接來看入口函數(shù):
/**
* This manages the execution of the main thread in an
* application process, scheduling and executing activities,
* broadcasts, and other operations on it as the activity
* manager requests.
*
* {@hide}
*/
public final class ActivityThread extends ClientTransactionHandler {
? ? public static void main(String[] args) {
? ? ? ? // 記錄開始,用于后續(xù)通過 systrace 檢查和調(diào)試性能問題。
? ? ? ? Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
? ? ? ? // 省略無關(guān)代碼
? ? ? ? // 為主線程創(chuàng)建消息隊(duì)列
? ? ? ? Looper.prepareMainLooper();
? ? ? ? // 省略無關(guān)代碼
? ? ? ? ActivityThread thread = new ActivityThread();
? ? ? ? thread.attach(false, startSeq);
? ? ? ? if (sMainThreadHandler == null) {
? ? ? ? ? ? sMainThreadHandler = thread.getHandler();
? ? ? ? }
? ? ? ? if (false) {
? ? ? ? ? ? Looper.myLooper().setMessageLogging(new
? ? ? ? ? ? ? ? ? ? LogPrinter(Log.DEBUG, "ActivityThread"));
? ? ? ? }
? ? ? ? // 記錄結(jié)束,后續(xù)可以通過 systrace 觀察這段代碼的執(zhí)行情況。
? ? ? ? // End of event ActivityThreadMain.
? ? ? ? Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
? ? ? ? // 開啟消息循環(huán)
? ? ? ? Looper.loop();
? ? ? ? // 主線程消息循環(huán)不會(huì)退出,如果走到這意味著發(fā)生意外,拋出異常。
? ? ? ? throw new RuntimeException("Main thread loop unexpectedly exited");
? ? }
}
代碼結(jié)構(gòu)和Android消息機(jī)制的通用示例很像,在里面看到了消息隊(duì)列的創(chuàng)建和消息循環(huán)的開啟,不同之處在于主線程中創(chuàng)建消息隊(duì)列使用的是Looper.prepareMainLooper:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself.? See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
? ? // 啟動(dòng)一個(gè)無法退出的消息循環(huán)
? ? prepare(false);
? ? synchronized (Looper.class) {
? ? ? ? if (sMainLooper != null) {
? ? ? ? ? ? throw new IllegalStateException("The main Looper has already been prepared.");
? ? ? ? }
? ? ? ? // 返回主線程 looper 對(duì)象
? ? ? ? sMainLooper = myLooper();
? ? }
}
為主線程創(chuàng)建的消息循環(huán)是無法退出的,因?yàn)檫@個(gè)消息循環(huán)要處理很多重要事務(wù),比如Activity生命周期的回調(diào)等,如果退出將導(dǎo)致異常,這點(diǎn)在后續(xù)講解Activity啟動(dòng)過程的時(shí)候再詳細(xì)解析。
4.2 內(nèi)存泄露
Java垃圾回收機(jī)制對(duì)于每個(gè)從事Java的開發(fā)者應(yīng)該都不陌生,我們也清楚并不是所有對(duì)象占用的內(nèi)存都可以被及時(shí)回收,如果垃圾回收器準(zhǔn)備回收某些對(duì)象,但是由于它們還被其他對(duì)象引用,那么這些對(duì)象就無法被回收,這也是內(nèi)存泄漏的主要原因。
使用Android消息機(jī)制時(shí)會(huì)不會(huì)導(dǎo)致內(nèi)存泄漏呢?首先來看一種常見的使用方法:
public class MainActivity extends Activity {
? ? private TextView mTextView = null;
? ? private Handler mMyHandler = null;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? // 初始化控件
? ? ? ? mTextView = (TextView) findViewById(R.id.sample_text);
? ? ? ? // 初始化 Handler 對(duì)象
? ? ? ? mMyHandler = new MyHandler();
? ? ? ? // 啟動(dòng)一個(gè)延遲消息,在 3000ms 后有 mMyHandler 執(zhí)行。
? ? ? ? mMyHandler.sendEmptyMessageDelayed(0, 3000);
? ? }
? ? private class MyHandler extends Handler {
? ? ? ? @Override
? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? // 執(zhí)行消息,更新主線程中的控件。
? ? ? ? ? ? if (mTextView != null) {
? ? ? ? ? ? ? ? mTextView.setText("execute message");
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? @Override
? ? public void onDestroy() {
? ? ? ? super.onDestroy();
? ? }
}
在這個(gè)示例中,MyHandler是以Activity內(nèi)部類的形式存在的,所以mMyHandler是需要持有外部類對(duì)象引用的,而mMyHandler又被其發(fā)送的Message對(duì)象以target的方式引用,最終的結(jié)果就是Activity間接被Message引用。由于這個(gè)Message需要在一定的延遲后被執(zhí)行,如果在這之前Activity退出,但是由于其引用被Message持有,導(dǎo)致無法被系統(tǒng)回收,進(jìn)而導(dǎo)致內(nèi)存泄露。
既然Activity被Message引用導(dǎo)致內(nèi)存泄露,那有沒有辦法不讓其持有引用呢?當(dāng)然可以,使用“靜態(tài)內(nèi)部類”就可以避免這種情況,因?yàn)椤办o態(tài)內(nèi)部類”不需要持有外部類對(duì)象的引用,來看示例代碼:
public class MainActivity extends Activity {
? ? private TextView mTextView = null;
? ? private Handler mMyHandler = null;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? mTextView = (TextView) findViewById(R.id.sample_text);
? ? ? ? // 初始化 Handler 對(duì)象,并把主線程控件作為參數(shù)傳入。
? ? ? ? mMyHandler = new MyHandler(mTextView);
? ? ? ? // 啟動(dòng)一個(gè)延遲消息,在 3000ms 后有 mMyHandler 執(zhí)行。
? ? ? ? mMyHandler.sendEmptyMessageDelayed(0, 3000);
? ? }
? ? private static class MyHandler extends Handler {
? ? ? ? // 通過弱引用的方式持有外部對(duì)象的變量。
? ? ? ? private WeakReference<TextView> mTextViewRef = null;
? ? ? ? // 初始化弱引用對(duì)象,此后就持有了正確的對(duì)象引用。
? ? ? ? public MyHandler(TextView textView) {
? ? ? ? ? ? mTextViewRef = new WeakReference<>(textView);
? ? ? ? }
? ? ? ? @Override
? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? // 執(zhí)行消息,更新主線程中的控件。
? ? ? ? ? ? if (mTextViewRef != null && mTextViewRef.get() != null) {
? ? ? ? ? ? ? ? mTextViewRef.get().setText("execute message");
? ? ? ? ? ? }
? ? ? ? }
? ? };
? ? @Override
? ? public void onDestroy() {
? ? ? ? super.onDestroy();
? ? ? ? // 退出時(shí)清空消息隊(duì)列中的消息
? ? ? ? mMyHandler.removeCallbacksAndMessages(null);
? ? }
}
通過“靜態(tài)內(nèi)部類”和“弱引用”的結(jié)合,既可以不持有外部類對(duì)象引用又可以訪問外部類對(duì)象的變量,并在Activity退出時(shí)又移除消息隊(duì)列中的消息,進(jìn)一步避免了內(nèi)存泄露的風(fēng)險(xiǎn)。
這只是其中一中避免內(nèi)存泄露的方法,肯定還有其他方法也可以達(dá)到目的,有興趣的同學(xué)可以自行研究。
5. 總結(jié)
本文講解了Android消息機(jī)制的使用方法、整體流程和每個(gè)階段的實(shí)現(xiàn)原理,在最后還提到主線程消息循環(huán)的創(chuàng)建以及錯(cuò)誤使用導(dǎo)致的內(nèi)存泄漏及避免方法,希望能對(duì)大家學(xué)習(xí)消息機(jī)制有所幫忙。
鏈接:https://juejin.im/post/5c91e3176fb9a070d4199f2b