Android消息機制大家都不陌生吊趾,想必大家也都看過Handler、Looper的源碼(看過就可以忽略下文咯穆碎,直接看后文的重點)牙勘,下面就整合一下這方面的資料,加深對這方面的印象所禀。
用法
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_TEXT_VIEW:
mTextView.setText("UI成功更新");
default:
super.handleMessage(msg);
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = new Message();
message.what = MESSAGE_TEXT_VIEW;
mHandler.sendMessage(message);
}
}).start();
Handler 機制架構(gòu)
從上圖可以看到方面,是圍繞 Handler、Message色徘、MessageQueue 和 Looper 進行的恭金。先介紹相關(guān)的概念
從開發(fā)角度看, Handler 是 Android 消息系統(tǒng)機制的上層接口褂策,這使得在開發(fā)過程中只需要和 Handler 交互即可横腿。另外, Handler 并不是專門用來更新 UI 的斤寂,只是經(jīng)常被開發(fā)者用來更新 UI 而已耿焊,但是不能忽略它的其他功能,例如進行耗時的 I/O 操作等遍搞。
疑問:為什么子線程不能更新 UI罗侯?
這是因為 ViewRootImpl 對 UI 操作進行了驗證
void checkThread() {
if (mThread != Thread.currentThread()) {//Thread.currentThread()是UI主線程
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
另外, Android 的 UI 空間不是線程安全的溪猿,如果在多線程中并發(fā)訪問可能會導致 UI 控件處于不可預期的狀態(tài)钩杰。
疑問:為什么不對 UI 控件的訪問加上鎖機制呢?
這是因為加鎖诊县,會導致 UI 訪問的邏輯變復雜讲弄;其次,鎖機制會降低 UI 訪問的效率依痊。
這也是為啥會存在 Hanlder 的原因避除。
MessageQueue:消息隊列,顧名思義,它的內(nèi)部存儲了一組消息驹饺,以隊列的形式對外提供插入和刪除的工作钳枕。但是其內(nèi)部是采用單鏈表來存儲消息列表缴渊。
Looper:循環(huán)(消息循環(huán))赏壹,以無限循環(huán)的形式去查找是否有新消息,有則處理衔沼,無則等待蝌借。
Handler 源碼分析及其原理
Handler 的構(gòu)造方法
Handler 的構(gòu)造方法有很多,核心的構(gòu)造方法如下
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {//默認是false指蚁,若為true菩佑,則會檢測當前handler是否是靜態(tài)類
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();//獲得了 Looper 對象
if (mLooper == null) {//如果是工作線程,就為空
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");//不能在未調(diào)用 Looper.prepare() 的線程創(chuàng)建 handler
}
mQueue = mLooper.mQueue;//mLooper對應的消息隊列
mCallback = callback;
mAsynchronous = async;
}
一個構(gòu)造方法凝化,Android 消息機制的三個重要角色全部出現(xiàn)了稍坯,分別是 Handler 、Looper 以及 MessageQueue搓劫。
mLooper = Looper.myLooper();//獲得了 Looper 對象
下面看看 Looper.myLooper() 方法是嘛
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal 是個嘛
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
好溫馨的提示瞧哟,定義在 Looper 中,是一個 static final 類型的 ThreadLocal<Looper> 對象(在 Java 中枪向,一般情況下勤揩,通過 ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的秘蛔,也訪問不到的陨亡,各個線程中訪問的是不同的對象。)至于 ThreadLocal 是個嘛深员,參考這里
大概說一下负蠕, ThreadLocal 是一個線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程中存儲數(shù)據(jù)倦畅,數(shù)據(jù)存儲以后虐急,只有在指定線程中可以獲得存儲的數(shù)據(jù),對于其他線程來說則無法獲取到數(shù)據(jù)滔迈。
對于 Handler 來說止吁,它需要獲取當前線程的 Looper,很顯然燎悍,Looper 的作用域就是線程并且不同線程具有不同的 Looper敬惦,這個時候通過 ThreadLocal 就可以輕松實現(xiàn) Looper 在線程中的存取。
根據(jù)提示谈山,看看 prepare() 方法
/** 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() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//一個線程只會有一個 Looper
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
這段代碼首先判斷 sThreadLocal 中是否已經(jīng)存在 Looper 了俄删,如果還沒有則創(chuàng)建一個新的 Looper 設(shè)置進去。下面看看 Looper 的構(gòu)造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
構(gòu)造方法可以看出,創(chuàng)建了一個 MessageQueue畴椰,傳入?yún)?shù)值為 true (子線程默認是true臊诊,why?后面有講到)斜脂;創(chuàng)建了一個當前 thread 的實例引用抓艳。很明顯,one looper only one MessageQueue
到此帚戳,就有一個疑問了:在 UI Thread 中創(chuàng)建 Handler 時沒有調(diào)用 Looper.prepare()玷或,但是卻能正常運行(但是,我們注意到片任,sThreadLocal.get() will return null unless you've called prepare())偏友,Why?
既然能正常運行对供,那么肯定是調(diào)用了 prepare 方法位他,但是,在哪里調(diào)用了呢产场,這就要看主線程 ActivityThread 鹅髓。首次啟動 Activity 時通過 Process.start 創(chuàng)建應用層程序的主線程,創(chuàng)建成功后進入到主線程 ActivityThread 的 main 方法中開始執(zhí)行涝动, main 方法有:
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
很明顯咯迈勋,秘密就在 prepareMainLooper() 里面(即使后面加了個 MainLooper,但也是個 prepare)
/**
* 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() {
prepare(false);//可以看出醋粟,UI thread傳入的是false
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
UI 線程中會始終存在一個 Looper 對象( sMainLooper 保存在 Looper 類中靡菇, UI 線程通過getMainLooper 方法獲取 UI 線程的 Looper 對象),從而不需要再手動去調(diào)用 Looper.prepare() 方法了米愿。如下 Looper 類提供的 get 方法:
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
到這里厦凤,上面疑問的答案就顯而易見了。同時育苟,如果在子線程實例化 Handler较鼓,就必須要先調(diào)用Looper.prepare() 方法才可以。
到此先初步總結(jié)下上面關(guān)于 Handler 實例化的一些關(guān)鍵信息违柏,具體如下:
在主線程中可以直接創(chuàng)建 Handler 對象博烂,而在子線程中需要先調(diào)用 Looper.prepare() 才能創(chuàng)建 Handler 對象,否則運行拋出 ”Can’t create handler inside thread that has not called Looper.prepare()” 異常信息漱竖。
每個線程中最多只能有一個 Looper 對象禽篱,否則拋出異常。
可以通過 Looper.myLooper() 獲取當前線程的 Looper 實例馍惹,通過 Looper.getMainLooper() 獲取主(UI)線程的 Looper 實例躺率。
一個 Looper 只能對應了一個M essageQueue 玛界。
一個線程中只有一個 Looper 實例,一個 MessageQueue 實例悼吱,可以有多個 Handler 實例慎框。
Handler 對象也創(chuàng)建好了,接下來就該發(fā)送消息了 mHandler.sendMessage(message);
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message 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 sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
嗯后添,繼續(xù)往下看咯
/**
* Enqueue a message into the message queue after all pending messages
* before (current time + delayMillis). You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
最終走到了 sendMessageAtTime 這個方法
/**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;//mQueue是在Handler實例化時構(gòu)造函數(shù)中實例化的
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
sendMessageAtTime() 方法接收兩個參數(shù)笨枯,其中 msg 參數(shù)就是我們發(fā)送的 Message 對象,而uptimeMillis 參數(shù)則表示發(fā)送消息的時間吕朵,它的值等于自系統(tǒng)開機到當前時間的毫秒數(shù)再加上延遲時間猎醇,如果調(diào)用的不是 sendMessageDelayed() 方法窥突,延遲時間就為0努溃。
而 mQueue 是在 Handler 實例化時構(gòu)造函數(shù)中實例化的,在 Handler 的構(gòu)造函數(shù)中可以看見 mQueue = mLooper.mQueue ;而 Looper 的 mQueue 對象上面分析過了阻问,是在 Looper 的構(gòu)造函數(shù)中創(chuàng)建的一個MessageQueue梧税。
最終的 MessageQueue 的 enqueueMessage() 方法是個嘛,下面看看
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這個方法首先將我們要發(fā)送的消息 Message 的 target 屬性設(shè)置為當前 Handler 對象(進行關(guān)聯(lián))称近;接著將 msg 與 uptimeMillis 這兩個參數(shù)都傳遞到 MessageQueue (消息隊列)的 enqueueMessage() 方法中第队,如下
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//上面的target已經(jīng)是handler對象,not null
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
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();//設(shè)置當前msg的狀態(tài)
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {//檢測當前頭指針是否為空(隊列為空)或者沒有設(shè)置when 或者設(shè)置的when比頭指針的when要前
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
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.
//幾種情況要喚醒線程處理消息:1)隊列是堵塞的 2)barrier刨秆,頭部結(jié)點無target 3)當前msg是堵塞的
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
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 將當前msg插入第一個比其when值大的結(jié)點前凳谦。
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue 消息隊列對于消息排隊是通過類似 C 語言的鏈表來存儲這些有序的消息的。其中的 mMessages 對象表示當前待處理的消息衡未;消息插入隊列的實質(zhì)就是將所有的消息按時間( uptimeMillis 參數(shù)尸执,也就是 when )進行排序。具體的操作方法就根據(jù)時間的順序調(diào)用 msg.next 缓醋,從而為每一個消息指定它的下一個消息是什么如失。
當然如果你是通過 sendMessageAtFrontOfQueue() 方法來發(fā)送消息的,它也會調(diào)用 enqueueMessage() 來讓消息入隊送粱,只不過時間為0褪贵,這時會把 mMessages 賦值為新入隊的這條消息,然后將這條消息的 next 指定為剛才的 mMessages 抗俄,這樣也就完成了添加消息到隊列頭部的操作脆丁。
通過上面了解到,消息入隊并不是按照我們的認知那樣:入隊入的是隊尾动雹,而是根據(jù)when 的大小來插入(頭插是因為when = 0))
這里有一個小問題槽卫,大家可以自行考慮一下:當我利用 sendMessageDelayed() 方法延遲一段時間發(fā)送后,立馬開啟一個死循環(huán)洽胶,不停的 sendMessageAtFrontOfQueue() 晒夹,那么裆馒,之前的延遲發(fā)送的消息還會被執(zhí)行到嗎?
到此丐怯,消息也通過 handler 發(fā)送了喷好,并且存到了 MessageQueue 中,那么读跷,系統(tǒng)怎么處理 message 呢梗搅?
我們知道 MessageQueue 的對象在 Looper 構(gòu)造函數(shù)中實例化的;一個 Looper 對應一個 MessageQueue效览,所以說 Handler 發(fā)送消息是通過 Handler 構(gòu)造函數(shù)里拿到的 Looper 對象的成員 MessageQueue 的enqueueMessage 方法將消息插入隊列无切,也就是說出隊列一定也與 Handler 和 Looper 和 MessageQueue有關(guān)系。
既然會涉及到出隊丐枉,那么肯定就有出隊的方法哆键,那么找來找去,就在 loop() 方法里面(為啥 UI Thread 沒有調(diào)用 loop瘦锹,loop 也會執(zhí)行呢籍嘹?一看就沒有好好的看剛才貼的代碼,明明已經(jīng)調(diào)用弯院,卻說沒有調(diào)用
Looper.prepareMainLooper();
...
Looper.loop();
下回看代碼用點心
)
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
可以看到 for (;;) {} 就是一個死循環(huán)辱士,然后不斷的調(diào)用 next 方法(出隊的方法)
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
同樣,可以看到 for (;;) {}听绳,一個死循環(huán)
它的簡單邏輯就是如果當前 MessageQueue 中存在 mMessages (即待處理消息)颂碘,就將這個消息出隊,然后讓下一條消息成為 mMessages椅挣,否則就進入一個阻塞狀態(tài)头岔,一直等到有新的消息入隊。
這里有個疑問贴妻,相信網(wǎng)友的回答應該能回答這個問題了切油。
另外,可以參考這里名惩,里面有對 epoll_wait 的介紹澎胡。
繼續(xù) loop 方法,每當有一個消息出隊就將它傳遞到 msg.target 的 dispatchMessage() 方法中娩鹉。其中這個 msg.target 其實就是當前 Handler 對象
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看見 dispatchMessage 方法中的邏輯比較簡單攻谁,具體就是檢查 Message 的 callback 是否為空,不為空弯予,就通過 handleCallback() 方法處理消息戚宦。 Message 的 callback 是一個 Runnable 對象,就是handler 的 post 方法所傳遞的 Runnable 參數(shù))不為空锈嫩,handleCallback() 方法受楼,如下
private static void handleCallback(Message message) {
message.callback.run();
}
在這里垦搬,把沒有講到的一個方法列一下
/**
* 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);
}
我們在使用的過程中,還可以采用上面的方法艳汽,那么問題來了猴贰,Runnable為何可以轉(zhuǎn)變?yōu)镸essage呢,答案就在 getPostMessage(r) 中河狐;
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
答案顯而易見了米绕,在結(jié)合上面的 dispatchMessage 方法,我們就更容易明白了馋艺。
否則栅干,就檢查 mCallback 是否為空,不為空就調(diào)用 Callback 的 handleMessage() 方法處理消息捐祠。mCallback 是一個接口碱鳞,如下
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
通過注釋可知,可以采用如下方式創(chuàng)建 Handler 對象: Handler handler = new Handler(callback)雏赦。對應的構(gòu)造方法如下
/**
* Constructor associates this handler with the {@link Looper} for the
* current thread and takes a callback interface in which you can handle
* messages.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Callback callback) {
this(callback, false);
}
Callback 的意義如同注釋一般:可以用來創(chuàng)一個 Handler 的實例但不需要派生出 Handler 的子類劫笙。
因為在日常開發(fā)過程中芙扎,創(chuàng)建 Handler 最常見的方式就是派生一個 Handler 的子類并重寫其handleMessage 方法來處理具體的消息星岗,而Callback給我們提供了另外一種使用 Handler 的方式,當我們不想派生子類時戒洼,就可以通過 Callback 實現(xiàn)俏橘。
最后,調(diào)用 Handler 的 handleMessage 方法來處理消息圈浇。
為什么handleMessage() 方法中可以獲取到之前發(fā)送的消息寥掐,這就是原因。
因此磷蜀,一個最標準的異步消息處理線程的寫法應該是這樣(下面這段代碼召耘,就在 Looper 類的注釋里面,注釋往往很有用的哦褐隆,谷歌程序猿的某些注釋也是很搞笑很認真的):
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();
}
}
現(xiàn)在再看 handler 的架構(gòu)圖污它,是不是就更清晰了。
當我們在子線程調(diào)用 loop.prepare() 和 loop() 方法后庶弃,最好調(diào)用 loop.quit() 方法退出衫贬,終止消息循環(huán),否則這個子線程就會一直處于等待狀態(tài)歇攻。那么 quit 方法如下
/**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
再找
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
我們知道固惯,在子線程中調(diào)用 preare 時
public static void prepare() {
prepare(true);
}
默認的是 true,也是就是說缴守,子線程是可以退出的葬毫,而在 UI Thread 中
public static void prepareMainLooper() {
prepare(false);//可以看出镇辉,UI thread傳入的是false
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
傳的 false,就是提示 UI Thread 是不可以退出的
回到 quit 方法繼續(xù)看贴捡,可以發(fā)現(xiàn)實質(zhì)就是對 mQuitting 標記置位摊聋,這個 mQuitting 標記在MessageQueue 的阻塞等待 next 方法中用做了判斷條件,所以可以通過 quit 方法退出整個當前線程的loop 循環(huán)栈暇。
到此整個 Android 的一次完整異步消息機制分析使用流程結(jié)束麻裁。
前面涉及到的幾個主要的類 Handler、Looper源祈、MessageQueue 和 Message 的關(guān)系如下所述:
- Handler 負責將 Looper 綁定到線程煎源,初始化 Looper 和提供對外 API(我們在應用層,只關(guān)注這個香缺,多么好的設(shè)計模式)手销。
- Looper 負責消息循環(huán)和操作 MessageQueue 對象。
- MessageQueue 實現(xiàn)了一個消息隊列图张,好比那抽水泵锋拖,源源不斷的處理 Message。
- Message 是一次業(yè)務中所有參數(shù)的載體(里面還涉及到 Message 池祸轮,感興趣的自己查閱源代碼)兽埃。
重點
如果您看到了這里,那么今天分析 Handler适袜、Loop 和 MessageQueue柄错,主要是為了引出下面的這個東西 BlockCanary, 一個 Android 平臺的一個非侵入式的性能監(jiān)控組件,項目中已經(jīng)打算性能優(yōu)化專項中引入并解決相關(guān)性能問題苦酱,為了了解其原理售貌,故整理了一下整個 Handler 的原理。該控件的相關(guān)說明在這里疫萤。
如果應用滑動卡頓颂跨,可以使用該控件進行監(jiān)控(作者實現(xiàn)這個控件的原理,相比大家看過就會了解扯饶,很佩服作者在消息機制中發(fā)現(xiàn)了這么一個方式能夠監(jiān)控性能恒削,同樣是看過源碼分析,差距還是很明顯的帝际,腦子不夠開竅哇)
參考資料
Android開發(fā)藝術(shù)探索[M]. 電子工業(yè)出版社, 2015.372-390
android在線程中創(chuàng)建handler應注意什么 #44
Android異步消息處理機制完全解析蔓同,帶你從源碼的角度徹底理解
補充
Android 定時器實現(xiàn)的幾種方式和removeCallbacks失效問題詳解
上面這個鏈接主要是針對項目中,偶現(xiàn)無法正常 remove 的情況蹲诀,有時候處理起來很費解斑粱,涉及到生命周期、多線程脯爪、是否會重建等比較多的方面则北,應該從代碼的健壯性矿微、項目的復雜性、線程切換尚揣、生命周期切換等很多方法考慮原因(具體問題具體分析涌矢,群策群力)。