概述
搞Android的不懂Handler消息循環機制,都不好意思說自己是Android工程師。面試的時候一般也都會問這個知識點,但是我相信大多數碼農肯定是沒有看過相關源碼的,頂多也就是網上搜搜,看看別人的文章介紹。學姐不想把那個萬能的關係圖拿出來討論。
學姐先從我們平時的使用方法引出這個機制,再結合源碼進行分析。
我們平時使用是這樣的:
//1. 主線程
Handler handler = new MyHandler();
//2. 非主線程
HandlerThread handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
//發送消息
handler.sendMessage(msg);
//接收消息
static class MyHandler extends Handler {
//對於非主線程處理消息需要傳Looper,主線程有默認的sMainLooper
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
那麼爲什麼初始化的時候,我們執行了1或2,後面只需要sendMessage就可處理任務了呢?學姐這裏以非主線程爲例進行介紹,handlerThread.start()的時候,實際上創建了一個用於消息循環的Looper和消息隊列MessageQueue,同時啓動了消息循環,並將這個循環傳給Handler,這個循環會從MessageQueue中依次取任務出來執行。用戶若要執行某項任務,只需要調用handler.sendMessage即可,這裏做的事情是將消息添加到MessaeQueue中。對於主線程也類似,只是主線程sMainThread和sMainLooper不需要我們主動去創建,程序啓動的時候Application就創建好了,我們只需要創建Handler即可。
我們這裏提到了幾個概念:
HandlerThread 支持消息循環的線程
Handler 消息處理器
Looper 消息循環對象
MessageQueue 消息隊列
Message 消息體
對應關係是:一對多,即(一個)HandlerThread、Looper、MessageQueue -> (多個)Handler、Message
class="wp-embedded-content" sandbox="allow-scripts" security="restricted" src="http://w3cschool.codecloud.net/android-training/index.html/embed#?secret=T759duF3w0" data-secret="T759duF3w0" width="500" height="461" title="嵌入的WordPress文章" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="box-sizing: border-box;">
源碼解析
1. Looper
(1)創建消息循環
prepare()用於創建Looper消息循環對象。Looper對象通過一個成員變量ThreadLocal進行保存。
(2)獲取消息循環對象
myLooper()用於獲取當前消息循環對象。Looper對象從成員變量ThreadLocal中獲取。
(3)開始消息循環
loop()開始消息循環。循環過程如下:
每次從消息隊列MessageQueue中取出一個Message
使用Message對應的Handler處理Message
已處理的Message加到本地消息池,循環複用
循環以上步驟,若沒有消息表明消息隊列停止,退出循環
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static Looper myLooper() {
return sThreadLocal.get();
}
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();
}
}
2. Handler
(1)發送消息
Handler支持2種消息類型,即Runnable和Message。因此發送消息提供了post(Runnable r)和sendMessage(Message msg)兩個方法。從下面源碼可以看出Runnable賦值給了Message的callback,最終也是封裝成Message對象對象。學姐個人認爲外部調用不統一使用Message,應該是兼容Java的線程任務,學姐認爲這種思想也可以借鑑到平常開發過程中。發送的消息都會入隊到MessageQueue隊列中。
(2)處理消息
Looper循環過程的時候,是通過dispatchMessage(Message msg)對消息進行處理。處理過程:先看是否是Runnable對象,如果是則調用handleCallback(msg)進行處理,最終調到Runnable.run()方法執行線程;如果不是Runnable對象,再看外部是否傳入了Callback處理機制,若有則使用外部Callback進行處理;若既不是Runnable對象也沒有外部Callback,則調用handleMessage(msg),這個也是我們開發過程中最常覆寫的方法了。
(3)移除消息
removeCallbacksAndMessages(),移除消息其實也是從MessageQueue中將Message對象移除掉。
public void handleMessage(Message msg) {
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
3. MessageQueue
(1)消息入隊
消息入隊方法enqueueMessage(Message msg, long when)。其處理過程如下:
待入隊的Message標記爲InUse,when賦值
若消息鏈表mMessages爲空爲空,或待入隊Message執行時間小於mMessage鏈表頭,則待入隊Message添加到鏈表頭
若不符合以上條件,則輪詢鏈表,根據when從低到高的順序,插入鏈表合適位置
(2)消息輪詢
next()依次從MessageQueue中取出Message
(3)移除消息
removeMessages()可以移除消息,做的事情實際上就是將消息從鏈表移除,同時將移除的消息添加到消息池,提供循環複用。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == 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("MessageQueue", e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.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.
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
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
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 (false) Log.v("MessageQueue", "Returning message: " + msg);
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("MessageQueue", "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;
}
}
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
4. Message
(1)消息創建
Message.obtain()創建消息。若消息池鏈表sPool不爲空,則從sPool中獲取第一個,flags標記爲UnInUse,同時從sPool中移除,sPoolSize減1;若消息池鏈表sPool爲空,則new Message()
(2)消息釋放
recycle()將消息釋放,從內部實現recycleUnchecked()可知,將flags標記爲InUse,其他各種狀態清零,同時將Message添加到sPool,且sPoolSize加1
/**
* 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) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
5. HandlerThread
由於Java中的Thread是沒有消息循環機制的,run()方法執行完,線程則結束。HandlerThread通過使用Looper實現了消息循環,只要不主動調用HandlerThread或Looper的quit()方法,循環就是一直走下去。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
}
總結
- 關鍵類:HandlerThread、Handler、Looper、MessageQueue、Messaga
- MessageQueue數據結構,鏈表。