概述
Handler主要用於執行緒間
的通訊,Handler主要是由MessageQueue
,Message
,Looper
,Handler
,共同組成,稱為Handler訊息機制,儲存Looper使用了ThreadLocal
,下面我們一次講解這幾個類
Handler
主要負責傳送訊息,和處理訊息MessageQueue
主要負責儲存訊息Looper
主要負責從MessageQueue
中取出訊息,然後分發給Handler
ThreadLocal
主要負責儲存不同執行緒的Looper
物件Message
主要負責儲存資料
ThreadLocal
ThreadLocal
是一個執行緒內部的資料儲存類,通過他可以在指定執行緒中儲存資料,資料儲存後,只有指定執行緒才可以可以獲取儲存資料,對於其他執行緒來說,則無法獲取到資料;一般來說當某些資料是以執行緒為作用域,且不同執行緒有不同副本的時候,就可以考慮採用ThreadLocal
,比如對於Handler
來說,他們需要獲取不同執行緒的Lopper
,這個時候就需要通過ThreadLocal
可以輕鬆在不同執行緒儲存Looper
ThreadLocal
另一個使用場景是複雜邏輯的物件傳遞,比如監聽器傳遞,有時候一個執行緒的任務過於複雜,這可能表現為函式作用棧比較深,以及程式碼入口的多樣性,在這種情況下,我們需要監聽器貫穿整個執行緒,這個時候就可以採用ThreadLocal
,讓監聽器作為執行緒的全域性物件而存在,執行緒內只要get就可以獲取監聽器
ThreadLocal的使用
mThreadLocal = new ThreadLocal<>();
mThreadLocal.set(true);
Log.d("mmm","當前執行緒"+Thread.currentThread()+"ThreadLocal儲存"+ mThreadLocal.get());
new Thread("thread1"){
@Override
public void run() {
super.run();
mThreadLocal.set(false);
Log.d("mmm","當前執行緒"+Thread.currentThread()+"ThreadLocal儲存"+ mThreadLocal.get());
}
}.start();
new Thread("thread2"){
@Override
public void run() {
super.run();
Log.d("mmm","當前執行緒"+Thread.currentThread()+"ThreadLocal儲存"+ mThreadLocal.get());
}
}.start();
複製程式碼
我在主執行緒設定了true
, thread1
設定了false
, thread2
沒有設定,按照正常來說獲取,主執行緒 為true
,thread1是false, thraed2為null
,看一下log
09-28 11:30:12.616 32536-32536/com.example.jh.rxhapp D/mmm: 當前執行緒Thread[main,5,main]ThreadLocal儲存true
09-28 11:30:12.618 32536-32745/com.example.jh.rxhapp D/mmm: 當前執行緒Thread[thread2,5,main]ThreadLocal儲存null
09-28 11:30:12.619 32536-32744/com.example.jh.rxhapp D/mmm: 當前執行緒Thread[thread1,5,main]ThreadLocal儲存false
複製程式碼
ThreadLocal原始碼
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
複製程式碼
set
方法就是通過當前執行緒獲取一個ThreadLocalMap
,然後通過ThreadLocalMap
去儲存資料,如果ThreadLocalMap
是null
那麼久同過當前thread
去建立一個ThreadLocalMap
,再去儲存,下面我們看一下ThreadLocalMap
是如何建立的
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
}
複製程式碼
每一個Thread
內部都有一個ThreadLocalMap
物件,如果這個物件為null,就為他重新賦值,然後我們看他是如何set
資料的
private void set(ThreadLocal key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
複製程式碼
首先用key
計算出陣列下標,然後從Entry[]
中取出值,如果有資料則重新賦值,如果沒有資料,則建立一個新的Entry
新增到Entry[]
陣列中
下面我們看一下get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
複製程式碼
首先獲取此執行緒的ThreadLocalMap
,如果不為Null
,就用key
計算出Entry[]
陣列下標,然後取出Entry
,然後再取出具體的值,如果ThreadLocalMap為Null
或者取出的Entry為Null
,就重新賦值
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
複製程式碼
ThreadLocal總結
每一個執行緒中都會有一個 ThreadLocal.ThreadLocalMap threadLocals = null;
成員變數,我們操作ThreadLocal的set個get方法
時,都是操作的單個執行緒中ThreadLocalMap
物件,而ThreadLocalMap
中是以Entry[]
陣列來儲存資料,所以就實現了每個執行緒都會有不同的值
Lopper
建立Lopper
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//一個執行緒只允許建立一個looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製程式碼
利用靜態prepare
方法,來建立Looper
,對於無參的情況,預設呼叫 prepare(true)
,表示Looper
允許退出,false
表示不允許退出,一個執行緒只允許建立一個Looper
,Looper
儲存在ThreadLocal
中,這樣就實現了一個執行緒一個Looper,建立Looper的時候還建立一個MessageQueue
prepareMainLooper
該方法主要在ActiityThread只使用,建立主執行緒的Looper
public static void prepareMainLooper() {
//該Looper不允許退出
prepare(false);
synchronized (Looper.class) {
//把該Looper設定為主執行緒Looper,只能設定一次
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//獲取主執行緒的Looper
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
複製程式碼
loop()
public static void loop() {
//獲取本執行緒的looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取MessageQueue
final MessageQueue queue = me.mQueue;
...
for (;;) {
//從MessageQueue中取出訊息,沒有訊息就會阻塞
Message msg = queue.next(); // might block
if (msg == null) {
// 一般情況msg不會為null,只有messageQueue退出,msg才會返回null
return;
}
...
//msg.target其實就是Handler物件,把訊息分發給Handler
msg.target.dispatchMessage(msg);
...
//把Message放入訊息池
msg.recycleUnchecked();
}
}
複製程式碼
loop()
方法進入無限迴圈,不斷重複以下操作
- 從
MessageQueue
中取出Message
- 把
Message
分發給對應的Handler
- 把分發後的
Message
回收到訊息池,以便重新利用
quit()
public void quit() {
//移除訊息
mQueue.quit(false);
}
public void quitSafely() {
//安全的移除訊息
mQueue.quit(true);
}
複製程式碼
Looper.quit()
,最終呼叫的是MessageQueue
的quit
方法,這倆個方法區別就是,一個quit
方法會,直接退出,quitSafely
會執行完剩餘的訊息退出
Looper總結
Looper
的主要工作是,從MessageQueue
中獲取訊息,然後分發給對應的Handler
,除了主執行緒,其他執行緒都需要自己去呼叫Looper.prepare()
方法建立Looper
,因為主執行緒的Looper
在ActivityThread
的main
方法裡面建立了,建立完成之後在呼叫Looper.loop
方法進行迴圈,下面是一個建立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();
}
}
複製程式碼
Handler
構造方法
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
...
//獲取此執行緒中的looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
//獲取looper中的MessageQueue
mQueue = mLooper.mQueue;
//是否設定了Callback
mCallback = callback;
//是否為非同步
mAsynchronous = async;
}
複製程式碼
這個幾個構造方法,最終都呼叫了倆個引數的構造方法,對於無參的構造方法,預設使用本當前執行緒中的looper
,callback
為null
,訊息為同步處理的方式
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
複製程式碼
以Looper
為引數的構造方法,可以指定Looper
傳送訊息
這是傳送訊息的呼叫鏈,我們發現最終都是呼叫了MessageQueue.enqueueMessage()
send
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
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賦值
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製程式碼
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;
return m;
}
複製程式碼
Handler.sendEmptyMessage()
系列方法,最終呼叫了MessageQueue.enqueueMessage(msg, uptimeMillis)
,將訊息新增到訊息佇列中,其中uptimeMillis
是系統時間加上延遲時間
分發訊息
在Looper.loop()方法中,發現有訊息,會呼叫msg.target.dispatchMessage方法,來分發訊息
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();
}
複製程式碼
分發流程
- 當
msg
中有callback
時,則呼叫message.callback.run()
;方法,其中的callback
指的Runnable
- 如果
callback
為空,那麼則看一下成員變數的mCallback
是否為空,這個是Handler
的構造方法傳入的 - 如果
mCallback
也為空,則呼叫handleMessage
方法,這個一般在Handler
的子類中重寫
其他方法
removeMessages
移除訊息,其實還是操作的MessageQueue,下面再一起分析
public final void removeMessages(int what, Object object) {
mQueue.removeMessages(this, what, object);
}
複製程式碼
Handler總結
Handler主要工作就是,傳送訊息,最終是把訊息插入到了MessageQueue中,然後通過Looper.loop方法,迴圈從MessageQueue拿出訊息,然後通過Handler把訊息分發出去,這就完成了一次迴圈
MessageQueue
MessageQueue是java層和c++層連結的紐帶,大部分的核心方法都是交給native層去做,MessageQueue中的native方法如下
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
複製程式碼
想要詳細瞭解這些native方法做了什麼情移步到gityuan
大神的部落格 Android訊息機制2-Handler(Native層)
建立MessageQueue
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//通過native程式碼初始化訊息佇列
mPtr = nativeInit();
}
複製程式碼
enqueueMessage 插入訊息
boolean enqueueMessage(Message msg, long when) {
//msg.target不能為空
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) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 如果p==null表示訊息佇列為空,或者msg訊息觸發時間為佇列最早,則把訊息插入頭部,如果阻塞喚醒佇列
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//按照時間順序插入到佇列中,不需要喚醒佇列
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
複製程式碼
MessageQueue的插入,其實就是連結串列的插入,是按照Message的觸發時間先後順序排列的,訊息頭是最早觸發的,當有訊息假如佇列時,會從頭開始遍歷,直到找到訊息應該插入的合適位置,以保證所有訊息的時間順序
next 獲取訊息
Message next() {
final long ptr = mPtr;
//如果訊息迴圈已經退出就直接返回null
if (ptr == 0) {
return null;
}
// 注意這裡首次迴圈為-1
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,等待nextPollTimeoutMillis時長,或者被喚醒都會返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
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 while迴圈遍歷訊息連結串列
// 跳出迴圈時,msg指向離表頭最近的一個非同步訊息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//當前時間小於下個訊息測觸發時間,就重新設定阻塞的時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//如果訊息佇列不為空,並且當前時間大於等於訊息的觸發時間,直接把訊息返回,然後從訊息佇列移除此訊息
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 {
//沒有訊息則把nextPollTimeoutMillis設定為-1
nextPollTimeoutMillis = -1;
}
// 如果訊息正在推出則返回null
if (mQuitting) {
dispose();
return null;
}
//這裡是idlehandler,注意這裡pendingIdleHandlerCount < 0才會進入,而等於0不會進入,什麼時候小於0呢,其實就是第一次進入迴圈,賦值為-1
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//注意這裡pendingIdleHandlerCount <= 0,小於等於0就直接continue,不會走下面的程式碼
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.
//上方是原始碼註釋,意思是,執行IdleHandler,但是隻會在第一次迭代執行
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.
//上邊是原始碼的註釋,意思就是把它重新賦值為0,也就意味著IdleHandler只執行一次
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;
}
}
複製程式碼
-
首先進入先判斷是否已經退出,退出直接返回,不退出進行下一步
-
之後再判斷當前的
MessageQueue
是否為空,為空則賦值阻塞時間nextPollTimeoutMillis = -1;
-
如果不為空,則判斷當前時間是否大於等於訊息的觸發時間,如果小於觸發時間,則賦值阻塞時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
-
如果當前時間大於等於觸發時間,則直接取出訊息返回,並且把此訊息移除佇列
-
其中涉及一個方法
nativePollOnce(ptr, nextPollTimeoutMillis);
這是一個native方法,主要作用是阻塞,nextPollTimeoutMillis
代表阻塞時間- 其中
nextPollTimeoutMillis=-1
表示,一直阻塞,直到被喚醒 - 其中
nextPollTimeoutMillis=0
表示,不阻塞,立即返回 - 其中
nextPollTimeoutMillis>0
表示,阻塞nextPollTimeoutMillis
毫秒,如果期間喚醒也會立即返回
- 其中
同步屏障
上方還涉及一個知識點,同步屏障,我們可以通過MessageQueue.postSyncBarrier
方法來設定
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
複製程式碼
這個方法就是建立一個Message
放到了訊息佇列中,好像沒有什麼特別的,其實這裡面有一個特殊點Message
沒有為Tagret
賦值
我們通常傳送的訊息呼叫Handler.sendMessage
都會早方法內部給Message.Tagret
賦值
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
//...
return queue.enqueueMessage(msg, uptimeMillis);
}
複製程式碼
同步屏障的作用
上面程式碼有註釋,當遇到了同步屏障,就會進行do while
迴圈,迴圈條件是msg != null && !msg.isAsynchronous()
,這個的意思就是跳過同步訊息,返回非同步訊息,也就是說,當有非同步訊息的時候優先執行非同步訊息
如何傳送非同步訊息
通常我們傳送的訊息都是同步訊息,如果逍遙傳送非同步訊息,只需要在Handler的構造方法傳入async=true
public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);
複製程式碼
IdleHandler
上方的原始碼繼續向下分析就是IdleHandler
,我之前寫的一篇文章Android LeakCanary的使用和原理
,LeakCanary中使用了IdleHandler
void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
複製程式碼
IdleHandler的作用是在當前執行緒訊息佇列空閒時,去做一些我們想要做的操作,但是IdleHandler只會執行一次,上面註釋已經描述的很清楚了
Message
Message主要包括以下資訊
資料型別 | 成員變數 | 解釋 |
---|---|---|
int | what | 訊息類別 |
long | when | 訊息觸發時間 |
int | arg1 | 引數1 |
int | arg2 | 引數2 |
Object | obj | 訊息內容 |
Handler | target | 訊息響應方 |
Runnable | callback | 回撥方法 |
訊息池
Message
維護了一個訊息池,recycle()
方法可以把用過的訊息假如到訊息池中,這樣做的好處是,當訊息池不為空時,可以直接從中取出Message
使用,而不是重新建立,提高效率
靜態變數sPool
的資料型別是Message
,其實是一個連結串列,維護這個訊息池,MAX_POOL_SIZE
代表容量,預設50
recycle()
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
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++;
}
}
}
複製程式碼
其實就是一個連結串列的插入,把資訊清除,然後插入
obtain() 從訊息池中獲取訊息
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();
}
複製程式碼
如果sPool不為null,就從池子了取出一個Message,如果為null,就直接New一個返回
Android中為什麼主執行緒不會因為Looper.loop()裡的死迴圈卡死
要完全徹底理解這個問題,需要準備以下4方面的知識:Process/Thread
,Android Binder IPC
,Handler/Looper/MessageQueue訊息機制
,Linux pipe/epoll機制
給大家推薦一個靠譜答案,還是gityuan大神的回答Android中為什麼主執行緒不會因為Looper.loop()裡的死迴圈卡死?
總結
- Handler通過sendMessage方法傳送訊息,插入到MessageQueue
- Looper通過loop方法迴圈取出Message,然後分發給Handler
- 然後通過dispatchMessage,交給相應的方法做處理
Android 開發藝術探索