Handler主要用於線程間
的通訊,Handler主要是由MessageQueue
,Message
,Looper
,Handler
,共同組成,稱爲Handler消息機制,存儲Looper使用了ThreadLocal
,下面咱們一次講解這幾個類java
Handler
主要負責發送消息,和處理消息MessageQueue
主要負責儲存消息Looper
主要負責從MessageQueue
中取出消息,而後分發給Handler
ThreadLocal
主要負責存儲不一樣線程的Looper
對象Message
主要負責存儲數據ThreadLocal
是一個線程內部的數據儲存類,經過他能夠在指定線程中儲存數據,數據存儲後,只有指定線程才能夠能夠獲取儲存數據,對於其餘線程來講,則沒法獲取到數據;通常來講當某些數據是以線程爲做用域,且不一樣線程有不一樣副本的時候,就能夠考慮採用ThreadLocal
,好比對於Handler
來講,他們須要獲取不一樣線程的Lopper
,這個時候就須要經過ThreadLocal
能夠輕鬆在不一樣線程存儲Looper
c++
ThreadLocal
另外一個使用場景是複雜邏輯的對象傳遞,好比監聽器傳遞,有時候一個線程的任務過於複雜,這可能表現爲函數做用棧比較深,以及代碼入口的多樣性,在這種狀況下,咱們須要監聽器貫穿整個線程,這個時候就能夠採用ThreadLocal
,讓監聽器做爲線程的全局對象而存在,線程內只要get就能夠獲取監聽器git
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源碼app
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
數據的async
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[]
數組中ide
下面咱們看一下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
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();
}
}
複製代碼
構造方法
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是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
,我以前寫的一篇文章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主要包括如下信息
數據類型 | 成員變量 | 解釋 |
---|---|---|
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一個返回
要徹底完全理解這個問題,須要準備如下4方面的知識:Process/Thread
,Android Binder IPC
,Handler/Looper/MessageQueue消息機制
,Linux pipe/epoll機制
給你們推薦一個靠譜答案,仍是gityuan大神的回答Android中爲何主線程不會由於Looper.loop()裏的死循環卡死?
Android 開發藝術探索