提到消息機制讀者應該都不陌生……從開發角度來講, Handler 是 Android 消息機制的上層接口,這使得在開發過程當中只須要和 Handler 交互便可。……經過它能夠輕鬆將一個任務切換到 Handler 所在的線程中去執行。java
正如開篇詞所說,「主線程中不能進行網絡通訊等耗時操做,而子線程中不能進行 UI 更新」,是我在 android 開發入門遇到的第一個知識點(keng),但當時只是單純記憶,本篇將順着開發藝術探索的講述,梳理 android 的消息機制有關知識。android
###開篇知識小點 Handler 是 Android 消息機制的上層接口,使用場景一般是更新 UI。 Android 消息機制主要指 Handler 的運行機制,Handler 的運行須要底層的 MessageQueue 和 Looper 的支持。數組
注意:線程是默認沒有 Looper 的,若是須要使用 Handler 就必須爲線程建立 Looper。主線程,即UI線程,是 ActivityThread ,ActivityThread 被建立時就會初始化Looper,因此主線程中默承認以使用 Handler。安全
###概述 幾乎全部的 Android 開發者都知道在 Android 中訪問 UI 只能在主線程中進行。CheckThread() 方法會對線程調用 UI 操做的正確性作出驗證,若是當前訪問 UI 的線程並不是主線程,則會拋出異常。 但, Android 建議不要在主線程使用耗時操做,以避免致使程序沒法響應,即ANR。在開發工做中,咱們經常會遇到須要從服務端拉取信息,並在 UI 中進行顯示。Handler 的存在就是爲了解決在子線程中沒法訪問 UI 的矛盾。網絡
方法書裏沒有介紹,翻出萌新筆記貼一點: 簡單應用:數據結構
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
// 在這裏能夠進行UI操做
break;
default:
break;
}
}
};
複製代碼
//在須要耗時操做的地方,開子線程
new Thread(new Runnable() {
@Override
public void run() {
//能夠進行耗時操做
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); //將Message對象發送出去
}
}).start();
複製代碼
過程以下:多線程
注意:在Activity中,並無顯式調用 Looper.prepare() 和Looper.loop() 方法,由於在 Activity 的啓動代碼中,已經在當前 UI 線程調用了Looper.prepare() 和 Looper.loop() 方法,這就是前文提到 UI 線程默承認以使用 Handler 的緣由。 runOnUiThread() 是一個異步消息處理機制的接口封裝,用法簡單但實際原理是同樣的。併發
Handler 建立時會採用當前線程的 Looper 來構建內部消息循環系統,若是當前線程沒有 Looper ,那麼就會報錯。 解決方法:爲當前線程建立 Looper ,或者在一個有 Looper 的線程中建立 Handler Handler 建立完畢以後,其內部的 Looper 以及 MessageQueue 就能夠和 Handler 一塊兒協同工做了,而後經過 Handler 的 post 方法將一個 Runnable 投遞到 Handler 內部的 Looper 中處理,也可經過 Handler 中的 send 發送消息,一樣在 Looper 內處理。post 的本質也是調用 send 。工做過程如圖: app
一個線程內部的數據存儲類,經過它能夠獨立存儲指定線程中的數據。平常開發中較少用到,可是 android 源碼中有時會利用它實現一些看似複雜的問題。less
// android SDK-27
boolean enqueueMessage(Message msg, long when) {
...
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;
}
複製代碼
實際上就是單鏈表插入操做。
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;
}
}
複製代碼
###Looper源碼解析 在構造方法中建立一個 MessageQueue ,而後將當前線程對象保存起來。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製代碼
經過Looper.prepare() 便可手動當前線程建立一個 Looper, 接着經過 Looper.loop() 開啓循環。 Looper 提供了 quit 和 quitSafely 兩種方法退出 Looper,前者直接退出,後者設定一個安全標記,等消息隊列內全部消息處理完畢以後纔會安全退出。若是在子線程裏手動建立了 Looper 在全部消息完成以後應該調用 quit 方法,不然這個子線程會一直處在等待狀態。
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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();
}
}
複製代碼
loop 方法是一個死循環,惟一跳出死循環的條件是 MessageQueue.next 方法返回 null 。當 Looper.quit 被調用,Looper 調用 MessageQueue.quit 或者 quitSafely 方法通知消息隊列退出。next 是一個阻塞方法,若是未接到新消息將一直等待,若是接到新消息,則交給 dispatchMessage 處理,這個方法是在 handler 建立的 Looper 中執行的。 ###Handler 的工做原理 ####發送消息的典型過程
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
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);
}
rivate boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
也就是說,Handler 調用 sendMessage 方法,依次調用 sendMessageDelayed,sendMessageAtTime,enqueueMessage 方法後,調用 queue.enqueueMessage 向消息隊列插入了一條消息,接下來,按上文分析, Looper 中調用 MessageQueue.next 方法獲得消息,最後將消息交給 Handler.dispatchMessage 處理。 實現以下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
Handler 處理消息過程以下:
private static void handleCallback(Message message) {
message.callback.run();
}
複製代碼
2.檢查 mCallback 是否爲 null ,不爲 null 就調用 mCallback 的 handlerMessage 方法
/** * 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);
}
複製代碼
經過Callback 能夠採用Handler handler = new handler(callback)
的方式建立 Handler 對象。callback 的意義在於能夠建立一個 Handler 實例但不須要派生 Handler 的子類。在平常開發中,最多見的方式就是派生一個 Handler 的子類而且重寫 handlerMessage 方法,當不想用該方式的時候能夠採用 callback 實現。
###主線程的消息循環 在主線程的入口方法中國,調用 Looper.prepareMainLooper() 來建立主線程的 Looper 以及 MessageQueue,並經過調用 Looper.loop() 來開啓循環。
public static void main(String[] args) {
……
Process.setArgV0("<pre-initialized>");
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"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
主線程消息循環開始以後,ActivityThread 還須要 Handler 來和消息隊列進行交互,這個 Handler 就是 AcitivityThread.H。 ActivityThread 經過 Application Thread 和 AMS 進行進程間通訊,AMS以進程間通訊的方式完成 ActivityThread 的請求後會回調 ApplicationThread 中的 Binder 方法,而後 ApplicationThread 會向 H 發消息,H 收到消息後將 ApplicationThread 中的邏輯切換到 ActivityThread 中執行,即切換到主線程中去執行。