聲明:Android不同API版本中同一個類的實現方法可能會有不同,本文是基於最新的API 23的源碼進行講解的。
Android的UI是線程不安全的,也就是說在子線程中操作主線程(UI線程)程序就會崩潰。想在子線程中更新UI就需要用到異步操作機制。
Android的異步操作主要有兩種,AsyncTask和Handler。AsyncTask的詳細講解和源碼分析在我的另一篇博客中進行了詳細的講解,本篇就來講解一下Handler的用法與源碼分析。
基本用法
handlerBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new MyTask(), 1, 1000);
}
});
private class MyTask extends TimerTask {
@Override
public void run() {
Message message = new Message();
message.what = 1;
mHandler.sendMessage(message);
}
}
Handler處理方法
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
EditText editText = (EditText)findViewById(R.id.editText_handler);
editText.setText("Handler Test " + i);
i++;
}
};
源碼分析
Message 消息,理解爲線程間交流的信息,處理數據後臺線程需要更新 UI ,則發送Message 內含一些數據給 UI 線程。
2. Handler
Handler處理者,是 Message 的主要處理者,負責 Message 的發送,Message 內容的執行處理。後臺線程就是通過傳進來的 Handler對象引用來 sendMessage(Message)。 而使用 Handler,需要 implement 該類的 handleMessage(Message)方法,它是處理這些 Message 的操作內容,例如 Update UI 。通常需要子類化 Handler 來實現 handleMessage方法。
3. Message Queue
Message Queue 消息隊列,用來存放通過 Handler 發佈的消息,按照先進先出執行。每個 message queue 都會有一個對應的 Handler。Handler 會向 messagequeue 通過兩種方法發送消息:sendMessage 或 post。這兩種消息都會插在 message queue 隊尾並按先進先出執行。但通過這兩種方法發送的消息執行的方式略有不同:通過 sendMessage發送的是一個 message 對象,會被 Handler 的 handleMessage()函數處理;而通過 post 方法發送的是一個 runnable 對象,則會自己執行。
4. Looper
Looper 是每條線程裏的 Message Queue 的管家。Android 沒有 Global 的MessageQueue,而 Android 會自動替主線程(UI 線程)建立 Message Queue,但在子線程裏並沒有建立 Message Queue。 所以調用 Looper.getMainLooper()得到的主線程的 Looper 不爲 NULL,但調用 Looper.myLooper()得到當前線程的 Looper 就有可能爲 NULL。
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
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());
}
}
<strong>mLooper = Looper.myLooper();</strong>
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Looper.myLooper()這個方法之後對mLooper進行了判斷,如果爲空就報錯"Can't create handler inside thread that has not called Looper.prepare()",接下來就看看這個方法裏到底有什麼。
/**
* 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();
}
原來就是返回一個當前線程的Looper啊,沒有的話就返回null。這也就解釋了爲什麼在子線程中必須先調用Looper.prepare()了。在主線程main()方法中已經調用了Looper.prepare(),所以在主線程中可以直接new一個Handler而不用事先調用Looper.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) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
先判斷一下sThreadLocal中有沒有Looper,沒有就創建一個。同時也可以發現一個線程中只能有一個Looper。Message
<span style="font-size:18px;"> public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}</span>
<span style="font-size:18px;"> public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}</span>
<span style="font-size:18px;">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);
}</span>
<span style="font-size:18px;">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(TAG, 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;
}</span>
用了一個mMessages對象表示當前待處理的消息。消息入隊就把所有的消息按照時間的順序排列,根據時間的順序調用msg.next來指定它的下一個消息。如果你用sendMessageAtFrontOfQueue()發送消息,系統也會調用enqueueMessage(),只不過時間爲0,並且把mMessages賦值爲新入隊的這條消息,然後將這條消息的next指定爲剛纔的mMessages,完成了添加消息到隊列頭部的操作。
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();
}
}
這裏有一個死循環,queue.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;
}
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果mCallback不爲空,則調用mCallback的handleMessage()方法,否則直接調用Handler的handleMessage()方法,並將消息對象作爲參數傳遞過去。一個標準的Handler
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();
}
}