但願你看完這篇文章後也能夠把流程本身講出來,而且每一個環節還能夠講出不少細節java
他的消息機制離不開Looper、MessageQueueweb
Looper
每一個線程只能持有一個,主要負責循環查看 MessageQueue
裏面是否有 msg
須要處理,並將須要處理的消息取出,交給 Handler
MessageQueue
是負責存放消息的,數據結構是一個單鏈表,這樣就能夠方便地插入或刪除 msg
具體流程通常是:api
Handler
發送一條msg
=> 本質是向MessageQueue
裏插入一條msg
,插入時候的依據是msg.when
=> SystemClock.uptimeMillis() + delayMillis
markdown
這條msg
被MessageQueue.next()
返回並交給Handler
去處理數據結構
next()
會在有同步屏障(msg.target==null
)的時候遍歷查找並返回最先的異步消息,並在移除屏障後,從頭取出並返回消息異步
Handler.dispatchMessage(msg)
會優先處理msg.callback
,若是msg.callback
爲空,就處理Handler.mCallback
,而後處理是msg
自己 msg.callback
是在調用Handler.post(Runnable)
時,裏面的Runnable
(runOnUIThread
,view.post(Runnable)
也用的是Handler.post(Runnable)
,Runnable
是同樣的)async
這是在不新增Handler
的狀況下,另外一種調用Handler
的方式(以下)ide
class MyHandlerCallBack: Handler.Callback {
override fun handleMessage(msg: Message?): Boolean {
TODO("Not yet implemented")
}
}
複製代碼
能夠看到他也有handleMessage
這個方法oop
目的就是讓主線程一直卡在這個死循環裏面post
由於Looper的做用就是在這個死循環裏面取出消息,而後交給Handler
處理
Android的生命週期,你瞭解的onCreate,onStop,onStart...... 等等都是由Handler
來處理的,都是在這個死循環裏面運行的
因此什麼Looper
死循環卡死主線程怎麼辦???
必須給我卡住!!!不卡住的話,消息就無法整了!!!
看下Android啓動的時候的源碼 Activitythread.java >> main()
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
想一想寫java的時候,main最後一行執行完了,不就完全玩完了嘛!!!
其實想都不用想,一直在看MessageQueue
裏面有沒有消息唄,太簡單了!
咋看?
答: 調用MessageQueue.next()
看下源碼 Looper.java >> loop()
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
複製代碼
很簡單,next()
返回Message
,msg.target.dispatchMessage()
處理Message
可是隊列裏沒消息就會返回null,這是錯誤的!!!具體往下看
Handler
發消息的時候,目的就是對msg
通過一系列操做,最終也只是調用enqueueMessage
插入隊列而已
看下源碼 Handler>>enqueueMessage()
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
return
直接調用Message的插入隊列方法
出隊就是next()
方法,以前已經見過了
Message
是按時間排序的,也就是msg.when
=> SystemClock.uptimeMillis() + delayMillis
msg.when
是Message
指望被處理的時間
SystemClock.uptimeMillis()
是開機到如今的時間,delayMills
是延遲時間,這個在sendMessageDelayed
方法裏直接能夠直接傳參
next()
就是按照時間順序處理MessageQueue
裏面的消息的
可是next()
裏有個概念叫 同步屏障
同步屏障,就是說,平時MessageQueue
都是處理同步消息,也就是按順序來,一個個出隊
同步屏障就是阻擋同步消息的意思
就是msg.target == null
的時候,MessageQueue
就會去找msg.isAsynchronous()
返回true
的msg
isAsynchronous,沒錯 ! 這是異步消息,就是優先級很高,須要馬上執行的消息,好比:更新View
值得注意的是,講Looper的時候,源碼next()
後面官方給咱們註釋了 // might block
可能阻塞,也就是說可能這個next()
也許會執行很久
next()
會阻塞?,何時阻塞?
now < msg.when
也就是時間還沒到,指望時間大於如今的時間
另外看第一行,只有ptr == 0
,纔會返回null
因此上面才說next()
不會由於沒消息而返回null
,原來返回null
的時候在這呢!
看下源碼,MessageQueue.java >> next()
@UnsupportedAppUsage
Message next() {
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) {
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 {
...
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;
}
...
}
...
}
}
複製代碼
代碼簡略了仍是有點多,彆着急,慢慢看
那pre
何時就是0
了呢?
答:quit()
了以後
看下源碼,Looper.java
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
複製代碼
能夠看到只是一個傳參不一樣而已,下面看看這個參數是幹嗎的
看下源碼,MessageQueue.java >> 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);
}
}
複製代碼
能夠看到,safe == true
,就移除將來的Message
safe == false
,就移除全部的Message
mQuiting
變成了true
,記住他咱們一下子會用到
而改變ptr的地方在這裏
在next()
裏面
裏面有個dispose
,找不到能夠ctrl+F
找一下
這裏只有在mQuiting == true
的時候,纔會調用
這就是改mPtr
的地方,而後下次next()
的時候就會返回null
了
咱們已經知道了在Looper
的死循環裏面,會將next()
返回的msg
交給Handler
,調用dispatchMessage()
dispatchMessage()
裏面會先判斷msg
是否是被post
過來的,由於post
要執行的邏輯在msg.callback
裏面,callback
是一個Runnable
,這可能不是很好理解
你能夠想一想runOnUIThread(Runnable)
,這裏的Runnable
就是上面的callback
, 他們都是調用了Handler.post(Runnable)
至於爲啥起個名叫callback
,我也納悶兒
這些msg
是會的邏輯是你重寫的handleMessage
那裏的邏輯
若是實現了Handler.Callback
這個Interface
,就會處理mCallback
的handleMessage
而不是Handler
本身的handleMessage
這是一個優先級策略,沒什麼好奇怪的
咱們看下源碼 => Handler.java >> dispatchMessage()
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
複製代碼
這就是Handler的消息機制了
接下來咱們講講Handler
的另外一個功能,切線程
Handler切線程使用的是ThreadLocal
ThreadLocal
是線程裏面的一個數據儲存類,用法相似map
,key
就是thread
可是他沒有提供,根據key
來找ThreadLocal
的Values
的方法,因此暴露的api
就只能讓你去get
當前線程的ThreadLocal
的Values
對象而已,就是key
——你本身無法做爲參數傳進去,只能是currentThread
若是你沒用過ThreadLocal
,我給你舉個例子
fun main() {
val booleanThreadLocal = ThreadLocal<Boolean>()
booleanThreadLocal.set(true)
println("in Thread[${Thread.currentThread().name}] booleanThreadLocal value = ${booleanThreadLocal.get()}")
thread(name = "thread#001") {
booleanThreadLocal.set(false)
println("in Thread[${Thread.currentThread().name}] booleanThreadLocal value = ${booleanThreadLocal.get()}")
}
thread(name = "thread#002") {
println("in Thread[${Thread.currentThread().name}] booleanThreadLocal value = ${booleanThreadLocal.get()}")
}
}
複製代碼
結果是這樣的:你能夠本身運行看看
in Thread[main] booleanThreadLocal value = true
in Thread[thread#001] booleanThreadLocal value = false
in Thread[thread#002] booleanThreadLocal value = null
複製代碼
話說回來,Handler怎麼經過ThreadLocal切線程的呢?
答案是:Looper
是放在ThreadLocal
裏的
回顧片頭的流程,Handler
將消息插入MessageQueue
,而後Looper
取出來,再還給Handler
,這種設計不止是爲了讓msg
能夠按順序處理,還可讓外部接口只有Handler
最關鍵的是,Looper
跟Handler
的觸發關係只有Looper
觸發Handler
,Handler
不會觸發Looper
所以Handler
把消息放在MessageQueue
以後,就在等着Looper
來給本身派發任務(msg
)
舉個例子:
線程A調用主線程的Handler
發一個消息
Handler
將這個消息插入MessageQueue
,此時其實還在線程A裏
只有Looper
在next()
調用msg.target.dispatchMessage()
時,就變成了主線程了
僅僅是由於Looper
在 主線程 而已