首先明白3個問題:Handler 是什麼?Handler 拿來幹什麼?Handler 怎麼使用?
前言:爲什麼會出現Handler?
我們都知道Android 的UI線程(即主線程)是線程不安全的,一切引起UI變化的操作均需在主線程中執行,而更新UI的操作往往是在一些耗時操作之後的,比如網絡請求、IO操作、文件讀寫等這些耗時操作必須在子線程執行,而Android的開發規範規定我們不能在子線程中訪問UI控件,否則會觸發程序異常,而Handler 就是爲了解決在子線程中訪問UI的這個問題而誕生的。
1、問題:爲什麼Android 的UI線程(主線程)是線程不安全的?爲什麼Android 採用單線程模型來處理UI操作?
答:android UI 中是由invalidate()來更新界面,而invalidate()方法是線程不安全。
詳解:
更新UI只能是主線程的工作,子線程更新UI是線程不安全的,所以android裏非主線程操作主UI就會報錯。這是因爲多線程中併發訪問可能會導致UI處於不可預期的狀態,即子線程可以有好多個,但如果每個子線程都直接對UI元素進行操作,界面會混亂不堪(例如多個線程同時操作一個控件可能會有衝突發生,控件不知道該執行哪一個的命令)。
單線程模型:線程會面臨安全問題,雖然可以通過加鎖機制來解決線程的安全問題,但是加鎖首先會使UI訪問的邏輯變得複雜,其次降低運行效率,鑑於這兩個缺點,最簡單且高效的做法就是採用單線程模型來處理UI操作,所以主線程(UI線程)並沒進行加鎖限制多線程訪問, 可能這就是“出於性能優化考慮”。
2、問題:既然沒有對多線程訪問進行限制,而且子線程依然有進行UI操作的需求,那麼該如何解決呢?
所以Android規定只能在主線程中進行UI元素的更改,即所有子線程如果要修改用戶界面,就必須先通知我(主線程),我來幫你們完成 。所以 android 的 UI 操作一定要在 UI 線程中執行。
Android的單線程模型有兩條原則:
1.不要阻塞UI線程。
2.不要在UI線程之外訪問Android UI 控件
拓展:
3、問題:Android是怎麼知道你是不是在子線程中訪問UI的?
答:ViewRootImpl 對UI 的操作進行了驗證,這個驗證是通過ViewRootImpl 的checkThread 方法來完成的,如下所示:
void checkThread(){
if(mThread!=Thread.currentThread()){
throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can
touch its views.");
}
}
前面介紹完 Handler 的由來和 Android 的UI 線程,現在開始我們的正菜,詳解Handler 機制:
Handler 是什麼?
Handler 是Android 消息機制的上層接口,它的主要作用是將一個任務切換到某個指定的線程中去執行,是爲了解決子線程中無法訪問UI的問題而設計的。說白了它就是一個傳遞消息、處理消息的工具,它和它的兩個兄弟 MessageQueue、Looper 齊心協力才能實現上述的功能。
Android 的消息機制主要是指 Handler 的運行機制和Handler所附帶的MessageQueue和Looper的工作過程,Handler 的運行需要底層的 MessageQueue(消息隊列)和Looper 的支撐。
Handler、Message、MessageQueue、Looper 之間的關係
1. Message(消息的載體)
Message是在線程之間傳遞的消息,它可以在內部攜帶少量的信息,用於在不同線程之間交換數據。上一小節中我們使用到了Message的what字段,除此之外還可以使用arg1和arg2字段來攜帶一些整型數據,使用obj字段攜帶一個Object對象。
2. Handler(消息的消費者,即發送和處理)
Handler顧名思義也就是處理者的意思,它主要是用於發送和處理消息的。發送消息一般是使用Handler的sendMessage()方法,而發出的消息經過一系列地輾轉處理後,最終會傳遞到Handler的handleMessage()方法中,最終存儲在MessageQueue中。Handler 的主要作用就是將一個任務切換到某個指定的線程中去執行。是爲了解決子線程中無法訪問UI 的問題。
3. MessageQueue(消息的存儲器)
MessageQueue是消息隊列的意思,它採用單鏈表的數據結構來存儲消息列表。它主要用於存放所有通過Handler發送的消息。這部分消息會一直存在於消息隊列中,等待被處理。每個線程中只會有一個MessageQueue對象。
4. Looper(消息的處理器)
Looper是每個線程中的MessageQueue的管家,調用Looper的loop()方法後,就會進入到一個無限循環當中,然後每當發現MessageQueue中存在一條消息,就會將它取出,並傳遞到Handler的handleMessage()方法中。每個線程中也只會有一個Looper對象。
打個比方:傳送帶傳送物品,Looper就是傳送帶的電機,它會無限的循環查詢傳送帶MessageQueue中存儲的物品,Message就是存儲消息的物品,由Handler在上游發送(sendMessage),也由Handler在下游取出消費(handleMessage)
拓展:Handler 如何獲取到當前線程的Looper?
ThreadLocal 可以在不同的線程中互不干擾的存儲並提供數據,通過ThreadLocal 可以輕鬆的獲取到每個線程的Looper,線程默認是沒有Looper的,如果需要使用Handler就必須爲線程創建Looper。主線程(即UI 線程)就是ActivityThread,ActivityThread 在被創建時就會初始化Looper,者也是在主線程中默認可以使用Handler 的原因。
Handler 的工作原理:Handler 創建時會採用當前線程的Looper來構建內部的消息循環系統,如果當前線程沒有Looper,那麼就會報錯,如下所示:
E/AndroidRuntime: FATAL EXCEPTION: Thread-50003
Process: com.example.cyf.myapplication, PID: 2223
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at com.example.cyf.myapplication.MainActivity$3$1.<init>(MainActivity.java:0)
at com.example.cyf.myapplication.MainActivity$3.run(MainActivity.java:56)
at java.lang.Thread.run(Thread.java:818)
怎麼解決:
1、爲當前線程創建一個Looper,一個線程只能有一個Looper 對象 ,
2、在一個有Looper 的線程中創建 Handler
Handler 創建完畢後,通過Handler 的post 方法將一個 Runnable 投遞到Handler 內部的Looper 中去處理(最終也是通過 send 方法來完成的),也可以通過 Handler 的 send 方法發送一個消息,這個消息同樣會在 Looper中處理。當Handler 的send 方法被調用時,他會調用MessageQueue 的 enqueueMessage 方法將這個消息放入消息隊列中,然後Looper 發現有新消息到來時,就會處理這個消息,最終消息中的Runnable 或者Handler 的 handleMessage 方法就會被調用,注意Looper 是運行在創建Handler所在的線程中的
MessageQueue 消息隊列的工作原理:
其主要包含兩個操作:插入和讀取(讀取本身會伴隨着刪除操作),其分別對應着 enqueueMessage 和next 方法,其中enqueueMessage 方法的作用是向消息隊列中插入一條消息,而next 的作用是從消息隊列中取出一條消息並將其從消息隊列中刪除。
MessageQueue 是通過一個單鏈表的數據結構來維護消息隊列的,單鏈表在插入和刪除上比較有優勢。
MessageQueue 的enqueueMessage 方法:
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;
}
其主要實現單鏈表的插入操作
MessageQueue 的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) {//退出消息隊列直接返回null,跳出循環
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;
}
}
從上可以看出 next 方法是一個無限循環方法,如果消息隊列中沒有消息,則next 方法會一直阻塞在這裏,當有新消息到來時,next 方法會返回這條消息,並將其從單鏈表中刪除。
Looper 的工作原理:
在Android 消息機制中扮演者消息循環的角色,就是不停地從MessageQueue 中查看是否有新的消息,如果有就立即處理,沒有則一直阻塞在哪裏,在Looper 的構造方法中,會創建一個 MessageQueue 消息隊列,並將當前的線程保存起來,其源碼如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Handler 的工作需要Looper,沒有Looper的線程就會報錯,Looper的創建通過 Looper.prepare() 爲當前線程創建一個Looper,通過Looper.loop() 開啓消息循環。如下所示:
new Thread("Thread#2"){
@Override
public void run() {
Looper.prepare();//爲Thread#2 構建Looper,在該方法中,會將當前線程的Looper 存儲在ThreadLocal 中
//爲Thread#2 構建Handler,Handler 中會通過 mLooper = Looper.myLooper(); 獲取當前線程的Looper
Handler handler = new Handler();
Looper.loop();//開啓Looper 消息循環
}
}.start();
Looper 還可通過prepareMainLooper 方法構建Looper,該方法主要是給主線程(ActivityThread) 創建Looper使用,其本質也是通過prepar 方法來實現的,由於主線程的Looper 比較特殊,故Looper 提供了一個 getMainLooper 方法,通過它可在任何地方獲取到主線程的Looper。
Looper的退出:
1、通過quit 方法退出Looper,該方法會直接退出Looper
2、通過quitSafely 方法退出Looper,quitSafely 只是設定了一個退出標記,然後把消息隊列中的已有的消息處理完畢後才安全的退出
Looper 的 quit 和 quitSafely 方法:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
MessageQueue 的 quit 方法
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);
}
}
只有當Looper 的quit 或者quitSafely 方法被調用時,Looper 就會調用MessageQueue 的quit 方法來通知消息隊列退出,當消息隊列被標記爲退出狀態時,會跳出 next 方法的死循環並返回null,MessageQueue 的next 方法就會返回 null,就會跳出loop 方法的死循環。即Looper 必須退出,否則looper 方法會一直循環下去。
Looper 退出後,通過Handler 發送的消息會失敗,此時Handler 的send 方法會返回 false。在子線程中如果手動爲其創建Looper,那麼在所有的事情完成以後應該調用quit 方法來終止消息循環,否則這個子線程就會一直處於等待的狀態,如果退出Looper 後,這個線程就會立刻被終止,因此建議不需要的時候終止Looper。
Looper 最重要的一個方法是loop 方法,只有調用了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) {//如果消息爲null, 即消息隊列已退出,loop退出循環,
// 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 在Handler 的enqueueMessage 方法中被賦值爲發送消息的handler對象,即調用handler 的dispatchMessage方法
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();
}
}
從上可知以下2點:
loop 方法會調用 MessageQueue 的 next 方法來獲取新的消息,當沒有消息時,next 方法會阻塞在哪裏,從而導致looper 方法阻塞在哪裏,如果MessageQueue 的 next 方法返回了新消息,Looper 就會通過 msg.target.dispatchMessage(msg) 處理這條消息,這裏的msg.target 是發送這條消息的 Handler 對象,這樣 Handler 發送的消息最終又交給他的 dispatchMessage方法來處理了,但這裏的 Handler 的dispatchMessage 方法是在創建 Handler 時 所使用的Looper 中執行的,這樣就成功的將代碼邏輯切換到指定的線程中去執行了
public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
private Printer mLogging;
public static void prepare() {
prepare(true);
}
//創建一個 Looper 對象,並存儲在當前線程的ThreadLocal 中
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 void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
/**
* 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) {//當msg 爲空時,退出
// 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 在Handler 的enqueueMessage 方法中被賦值爲發送消息的handler對象,即調用handler 的dispatchMessage方法
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();
}
}
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
//從ThreadLocal 中獲取當前線程存儲的 Looper 對象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
/**
* Return the {@link MessageQueue} object associated with the current
* thread. This must be called from a thread running a Looper, or a
* NullPointerException will be thrown.
*/
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//創建一個 MessageQueue 消息隊列
mThread = Thread.currentThread();//將當前的線程保存起來
}
/**
* Returns true if the current thread is this looper's thread.
*/
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
/**
* Control logging of messages as they are processed by this Looper. If
* enabled, a log message will be written to <var>printer</var>
* at the beginning and ending of each message dispatch, identifying the
* target Handler and message contents.
*
* @param printer A Printer object that will receive log messages, or
* null to disable message logging.
*/
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
/**
* 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);
}
/**
* Quits the looper safely.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </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>
*/
public void quitSafely() {
mQueue.quit(true);
}
/**
* Gets the Thread associated with this Looper.
*
* @return The looper's thread.
*/
public @NonNull Thread getThread() {
return mThread;
}
/**
* Gets this looper's message queue.
*
* @return The looper's message queue.
*/
public @NonNull MessageQueue getQueue() {
return mQueue;
}
/**
* Dumps the state of the looper for debugging purposes.
*
* @param pw A printer to receive the contents of the dump.
* @param prefix A prefix to prepend to each line which is printed.
*/
public void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ");
}
@Override
public String toString() {
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+ ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
}
Handler 的工作原理:
Handler 的主要工作包含消息的發送和接收過程。消息的發送可以通過post 的一些列方法及send 的一系列方法來實現,post 的一系列方法最終也是通過send 的一系列方法來實現的,發送一條消息的典型過程如下所示:
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//將發送消息的Handler 賦值給 msg.target
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//調用 MessageQueue 的enqueueMessage 方法插入一條消息
}
post 方法:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;//將 Runnable 對象賦值給 m.callback
return m;
}
從上可以發現,Handler 發送消息的過程僅僅是向消息隊列中插入了一條消息,並將發送消息的Handler 賦值給 msg.target,MessageQueue 的next 方法就會返回這條消息給Looper,Looper 接收到消息後就開始處理了,最終消息由 Looper 交給 Handler 處理,即Handler 的 dispatchMessage 方法會被調用,這時 Handler就進入了處理消息的階段,dispatchMessage 的實現如下所示:
final Callback mCallback;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler 處理消息的過程如下:
首先,檢查Message 的 callback 是否爲 null, 不爲null 就通過 handlerCallback來處理消息,Message 的callback 是一個 Ruannable 對象,實際上就是 Handler 的post 方法所傳遞的 Runnable 參數, handlerCallback 的邏輯如下所示:
private static void handleCallback(Message message) {
message.callback.run();
}
其次檢查mCallback 是否爲 null,不null 就調用 mCallback 的 handlerMessage 方法來處理消息。Callback 是一個接口,其定義如下:
通過 Callback 可以通過如下方式來創建 Handler 對象:
Handler handler = new Handler(callback);
Callback 的意義就是可以用來創建一個Handler的實例但不需要派生Handler 的子類,在日常開發中,創建Handler 的最常見的方式就是派生一個Handler 的子類並重寫其 handlerMessage 方法來處理具體的消息,當我們不想派生子類時,就可以通過Callback 來實現。最後調用Handler 的handleMessage 方法來處理消息。
Handler 處理消息的過程可以歸納爲一個流程圖,如下所示:
Handler 還有一個特殊的構造方法,可以通過一個特定的 Looper 來構造 Handler,它的實現如下:
public Handler(Looper looper) {
this(looper, null, false);
}
Handler 的默認構造方法如下:
public Handler() {
this(null, false);
}
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());
}
}
mLooper = Looper.myLooper();
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 則會拋出"Can't create handler inside thread that has not called Looper.prepare()" 這個異常,即沒有Looper在子線程中構建Handler 會報錯的原因
Handler 的完整源碼如下:
public class Handler {
/*
* Set this flag to true to detect anonymous, local or member classes
* that extend this Handler class and that are not static. These kind
* of classes can potentially create leaks.
*/
private static final boolean FIND_POTENTIAL_LEAKS = false;
private static final String TAG = "Handler";
/**
* 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);
}
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
/**
* 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);
}
}
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
/**
* 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);
}
/**
* Use the provided {@link Looper} instead of the default one.
*
* @param looper The looper, must not be null.
*/
public Handler(Looper looper) {
this(looper, null, false);
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
*/
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
/**
* Use the {@link Looper} for the current thread
* 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 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(boolean async) {
this(null, async);
}
/**
* 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) {
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();
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;
}
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages. Also 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 looper The looper, must not be null.
* @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(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
/**
* Returns a string representing the name of the specified message.
* The default implementation will either return the class name of the
* message callback if any, or the hexadecimal representation of the
* message "what" field.
*
* @param message The message whose name is being queried
*/
public String getMessageName(Message message) {
if (message.callback != null) {
return message.callback.getClass().getName();
}
return "0x" + Integer.toHexString(message.what);
}
/**
* Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
* creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
* If you don't want that facility, just call Message.obtain() instead.
*/
public final Message obtainMessage()
{
return Message.obtain(this);
}
/**
* Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message.
*
* @param what Value to assign to the returned Message.what field.
* @return A Message from the global message pool.
*/
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
/**
*
* Same as {@link #obtainMessage()}, except that it also sets the what and obj members
* of the returned Message.
*
* @param what Value to assign to the returned Message.what field.
* @param obj Value to assign to the returned Message.obj field.
* @return A Message from the global message pool.
*/
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
/**
*
* Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned
* Message.
* @param what Value to assign to the returned Message.what field.
* @param arg1 Value to assign to the returned Message.arg1 field.
* @param arg2 Value to assign to the returned Message.arg2 field.
* @return A Message from the global message pool.
*/
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
/**
*
* Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
* returned Message.
* @param what Value to assign to the returned Message.what field.
* @param arg1 Value to assign to the returned Message.arg1 field.
* @param arg2 Value to assign to the returned Message.arg2 field.
* @param obj Value to assign to the returned Message.obj field.
* @return A Message from the global message pool.
*/
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
/**
* 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);
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <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.
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @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. Note that a
* result of true does not mean the Runnable 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 postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* at a specific time given by <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.
* The runnable will be run on the thread to which this handler is attached.
*
* @param r The Runnable that will be executed.
* @param uptimeMillis The absolute time at which the callback should run,
* using the {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @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. Note that a
* result of true does not mean the Runnable will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*
* @see android.os.SystemClock#uptimeMillis
*/
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
/**
* Causes the Runnable r to be added to the message queue, to be run
* after the specified amount of time elapses.
* The runnable will be run on the thread to which this handler
* is attached.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* Time spent in deep sleep will add an additional delay to execution.
*
* @param r The Runnable that will be executed.
* @param delayMillis The delay (in milliseconds) until the Runnable
* 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. Note that a
* result of true does not mean the Runnable 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 postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
/**
* Posts a message to an object that implements Runnable.
* Causes the Runnable r to executed on the next iteration through the
* message queue. The runnable will be run on the thread to which this
* handler is attached.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @param r The Runnable that will be executed.
*
* @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 postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
/**
* Runs the specified task synchronously.
* <p>
* If the current thread is the same as the handler thread, then the runnable
* runs immediately without being enqueued. Otherwise, posts the runnable
* to the handler and waits for it to complete before returning.
* </p><p>
* This method is dangerous! Improper use can result in deadlocks.
* Never call this method while any locks are held or use it in a
* possibly re-entrant manner.
* </p><p>
* This method is occasionally useful in situations where a background thread
* must synchronously await completion of a task that must run on the
* handler's thread. However, this problem is often a symptom of bad design.
* Consider improving the design (if possible) before resorting to this method.
* </p><p>
* One example of where you might want to use this method is when you just
* set up a Handler thread and need to perform some initialization steps on
* it before continuing execution.
* </p><p>
* If timeout occurs then this method returns <code>false</code> but the runnable
* will remain posted on the handler and may already be in progress or
* complete at a later time.
* </p><p>
* When using this method, be sure to use {@link Looper#quitSafely} when
* quitting the looper. Otherwise {@link #runWithScissors} may hang indefinitely.
* (TODO: We should fix this by making MessageQueue aware of blocking runnables.)
* </p>
*
* @param r The Runnable that will be executed synchronously.
* @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
*
* @return Returns true if the Runnable was successfully executed.
* Returns false on failure, usually because the
* looper processing the message queue is exiting.
*
* @hide This method is prone to abuse and should probably not be in the API.
* If we ever do make it part of the API, we might want to rename it to something
* less funny like runUnsafe().
*/
public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
/**
* Remove any pending posts of Runnable r that are in the message queue.
*/
public final void removeCallbacks(Runnable r)
{
mQueue.removeMessages(this, r, null);
}
/**
* Remove any pending posts of Runnable <var>r</var> with Object
* <var>token</var> that are in the message queue. If <var>token</var> is null,
* all callbacks will be removed.
*/
public final void removeCallbacks(Runnable r, Object token)
{
mQueue.removeMessages(this, r, token);
}
/**
* 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);
}
/**
* Sends a Message containing only the what value.
*
* @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 sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
/**
* Sends a Message containing only the what value, to be delivered
* after the specified amount of time elapses.
* @see #sendMessageDelayed(android.os.Message, long)
*
* @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 sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
/**
* Sends a Message containing only the what value, to be delivered
* at a specific time.
* @see #sendMessageAtTime(android.os.Message, long)
*
* @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 sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
/**
* 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);
}
/**
* 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;
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);
}
/**
* Enqueue a message at the front of the message queue, to be processed on
* the next iteration of the message loop. You will receive it in
* {@link #handleMessage}, in the thread attached to this handler.
* <b>This method is only for use in very special circumstances -- it
* can easily starve the message queue, cause ordering problems, or have
* other unexpected side-effects.</b>
*
* @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 sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
/**
* Remove any pending posts of messages with code 'what' that are in the
* message queue.
*/
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
/**
* Remove any pending posts of messages with code 'what' and whose obj is
* 'object' that are in the message queue. If <var>object</var> is null,
* all messages will be removed.
*/
public final void removeMessages(int what, Object object) {
mQueue.removeMessages(this, what, object);
}
/**
* Remove any pending posts of callbacks and sent messages whose
* <var>obj</var> is <var>token</var>. If <var>token</var> is null,
* all callbacks and messages will be removed.
*/
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
/**
* Check if there are any pending posts of messages with code 'what' in
* the message queue.
*/
public final boolean hasMessages(int what) {
return mQueue.hasMessages(this, what, null);
}
/**
* Check if there are any pending posts of messages with code 'what' and
* whose obj is 'object' in the message queue.
*/
public final boolean hasMessages(int what, Object object) {
return mQueue.hasMessages(this, what, object);
}
/**
* Check if there are any pending posts of messages with callback r in
* the message queue.
*
* @hide
*/
public final boolean hasCallbacks(Runnable r) {
return mQueue.hasMessages(this, r, null);
}
// if we can get rid of this method, the handler need not remember its loop
// we could instead export a getMessageQueue() method...
public final Looper getLooper() {
return mLooper;
}
public final void dump(Printer pw, String prefix) {
pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
if (mLooper == null) {
pw.println(prefix + "looper uninitialized");
} else {
mLooper.dump(pw, prefix + " ");
}
}
@Override
public String toString() {
return "Handler (" + getClass().getName() + ") {"
+ Integer.toHexString(System.identityHashCode(this))
+ "}";
}
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
private static void handleCallback(Message message) {
message.callback.run();
}
final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;
IMessenger mMessenger;
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();
} finally {
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
while (!mDone) {
try {
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
}
主線程的消息循環:
Android 的主線程就是ActivityThread,主線程的入口方法爲 main,在main 方法中系統會通過 Looper.preparMainLooper() 來創建主線程的Looper 記憶MessageQueue,並通過Looper.loop() 來開啓主線程的消息循環,過程如下所示:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
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 就是ActivityThread.H,它的內部定義了一組消息類型,主要包含了四大組件的啓動和停止等過程,如下所示:
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
public static final int SEND_RESULT = 108;
public static final int DESTROY_ACTIVITY = 109;
public static final int BIND_APPLICATION = 110;
public static final int EXIT_APPLICATION = 111;
public static final int NEW_INTENT = 112;
public static final int RECEIVER = 113;
public static final int CREATE_SERVICE = 114;
public static final int SERVICE_ARGS = 115;
public static final int STOP_SERVICE = 116;
...
}
ActivityThread 通過 ApplicationThread 和AMS 進行進程間通信,AMS 以進程間通信的方式完成 ActivityThread 的請求後會回調 ApplicationThread 中的Binder 方法,然後 ApplicationThread 會向 H 發送消息,H收到消息後會將ApplicationThread 中的邏輯切換到 ActivityThread 中去執行,即切換到主線程中去執行,這個過程就是主線程的消息循環模型。
總結:
1、創建Handler 時,會優先檢查是否有Looper,如果沒有則拋出異常,由於主線程即ActivityThread 在創建時就構建了Looper(通過Looper.prepareMainLooper 構建),所以在主線程中構建Handler 不會拋異常,如果在子線程中,在構建Handler 之前必須先創建Looper (通過Looper.preparLooper 創建),創建好Handler 後,還需啓動Looper。
2、構建Looper 時,會創建一個 MessageQueue ,並將該Looper 存儲在當前線程的 ThreadLocal 中,通過 Looper.looper 開啓輪詢,不停的查找 MessageQueue 中的消息,如果存在未處理的消息,則通過 Message 中的 tag 獲取關聯的 Handler,並調用 Handler.dispatchMessage 方法處理消息,該方法會最終調用 handlerMessage 方法或者Runable 中 的方法處理消息。
3、Handler 通過 sendMessage 或者 post 方法發送消息,首先會將自己賦值給 Message 的tag,並調用MessageQueue 中的enqueueMessage 方法向MessageQueue 中插入一條消息,Looper 通過MessageQueue 的next 方法獲取MessageQueue 中的消息,獲取後並將之從 MessageQueue 中刪除,如果 MessageQueue 中沒有消息,則MessageQueue 、Looper 都會被阻塞掛起。