目錄
繼上一篇 Handler(上)那些事兒之後,我們已經瞭解了ThreadLocal、MessageQueue、Looper三者之間的聯繫,帶着上一遍的疑問,我們來了解一下Handler源碼部分。
Handler
Handler 構造方法
上訴構造方法來自與api28,不過沒什麼大區別.相比大家在開發中用到做多的應該是無參構造,我們先來看一看源碼
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 " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
我們可以看到無參構造方法內部其實調用了另一個構造方法.主要就是給mLooper mQueue mCallback mAsynchronous 賦值,分別表示當前線程綁定的Looper,當前線程綁定的Looper內部的MessageQueue消息隊列,回調,是否異步.(異步本章節不作講解)
乘勝追擊,我們再看來來其他的構造方法:
public Handler(Callback callback) {
this(callback, false);
}
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(boolean async) {
this(null, async);
}
我們可以看到內部其實最後都是調用Handler(CallBack,async)/Handler(Looper,CallBack,async)這兩個構造方法,該方法主要就是進行mLooper mQueue mCallback mAsynchronous的賦值.
總結就是
- 若mLooper爲空則默認過去當前線程綁定的Looper,不爲空則用傳入的Looper
- mQueue則是通過Looper內部的MessageQueue獲取的
- mCallback默認爲null
- mAsynchronous默認爲false
所以這就解釋了平時在子線程和主線程通信的時候,對於Handler的使用我們就只是通過無參構造方法創建,因爲內部會獲取當前線程也就是主線程的Looper,所以可以通過Handler去改變UI.
sendMessage/postxxx
通過sendMessage/postxxx方法可以去分發消息,但是Handler給我們提供了很多sendMessage和postxxx方法,有的作用是延時作用,有的時候傳一個empty類型的Message,在此都不會一一分析,大家可以自行看看源碼,但是所有的方法最後都會去調用sendMessageAtTime我們來看看.
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;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
便於理解保留sendMessageAtTime調用前的一個方法,便於理解.
- sendMessageAtTime(message,uptimeMillis),uptimeMillis表示具體執行的時間
- 所以可以看到sendMessageDelayed方法最終調用了sendMessageAtTime,傳入的第二參數表示時間就是當前時間+加上延時的時間(若有延時則加,無則不加)
- sendMessageAtTime內部又調用了enqueueMessage,將message添加到隊列中,通過調用MessageQueue#enqueueMessage方法.
那麼後面的工作就交給了Looper#loop方法,去循環的調用MessageMessage#next方法.
在講解Looper#loop的時候,提到將MessageMessage#next返回的Message,通過message.target#dispatchMessage方法回調該信息,那麼我們來看看dispatchMessage方法.
dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
其實dispatchMessage主要決定消息回調的先後順序,
- 若msg.callback不爲空則回調handleCallBack
- 若mCallBack不爲空則回到mCallBack.handleMessage
- 最後都不會掉則回調Handler的handleMessage
msg.callback其實就是在構建Message的時候傳入的Runnable
mCallBack其實就是Handler構造方法中傳入的Callback
所以通過dispatchMessage,我們可以知道去獲取Message的途徑.
使用實例
public class MainActivity extends AppCompatActivity {
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread() {
@Override
public void run() {
super.run();
Looper.prepare();
handler = new Handler(Looper.myLooper(), callback);
Looper.loop();
}
}.start();
findViewById(R.id.btn_add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message = Message.obtain();
message.obj = "hello";
handler.sendMessage(message);
}
});
System.out.println("main " + Thread.currentThread().getName());
}
Handler.Callback callback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
System.out.println("THread " + msg.obj.toString() + Thread.currentThread().getName());
return false;
}
};
}
上訴代碼就是一種子線程Handler的使用. 那麼有人會問爲什麼平時主線程Handler用不到Looper#prepare和Looper#loop? 因爲Activity在創建的時候就幫我做了.
總結
這麼一來整一個Handler的消息機制就都瞭解了,最後咱們還是要總結歸納整個流程.
- 首先需要通過Looper#prepare去初始化該線程的MessageQuene
- 通過創建Handler並獲取當前線程的Looper,從而獲取Looper的MessageQuene
- 通過sendMessage,將所要分發的Message添加到MessageQueue中.
- 由於Looper#loop會去不斷的循環調用MessageQueue#next方法去獲取可以分發的Message
- 最後通過Message.target#dispatchMessage去回調Message.
爲了便於理解整個過程,我將Handler過程視爲一個傳送帶的工作流程.
Handler就是工人,負責貨物(Message)的組裝,將貨物(Message)拖放到傳送帶(MessageQuene)中,傳送帶(MessageQuene)被動力(Looper)驅使着去傳送貨物(Message),最終傳送到終點再有工人(Handler)去組裝.
進階部分
儲存Message是通過什麼方式存儲的.
- 存儲結構是以單鏈表的形式存儲
- 存儲是依據執行Message的最終實際時間的先後去存儲的.
MessageQueue線程喚醒/休眠
我們先來看看MessageQueue的next方法
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) {
(1) 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;
}
if (mQuitting) {
dispose();
return null;
}
(2) 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;
}
...
}
注:
mBlocked:表示線程是否休眠,該變量會在enqueueMessage中用到。
mMessages:表示當前執行的Message。
我們來看看(1)處代碼
- 判斷當前msg.when也就是執行message時間和now(當前時間)進行對比
- 若now>msg.when,則返回當前msg,並且mBlocked設置爲false,因爲當前時間大於可執行時間,所以不需要休眠等待,故線程當前狀態爲未休眠。
- 若now<msg.when則會記錄下nextPollTimeoutMillis(距離執行msg的時間差),進行線程休眠。
那麼記錄下nextPollTimeoutMillis(時間差)有什麼用呢? 大家知道looper.loop內部是有一個死循環去獲取msg的,並且msg單鏈的排列又是按照時間排列,所以只有執行完當前msg再回輪到下一個,那麼已知下一個msg執行時間和當前時間的差距,系統過讓線程休眠,休眠的時間則爲執行的時間差,nativePollOnce方法就是讓線程休眠nextPollTimeoutMillis的時間,這樣就避免的資源的浪費.
我們可以看到(2)處代碼
- 若當前沒有可執行的message或者是執行message的時間未到,沒有可執行的任務所以需要將mBlocked賦爲true。
大家想一想這一種情況,若當前時間爲12點,下一個執行的msgA爲12.30,因爲有時間差所以線程休眠,那麼此時我添加一個msgB他的執行時間爲12.15,可是線程還休眠着無法執行msgB,所以需要喚醒線程,將msgB插入到msgA前,然後修改休眠時間進行休眠.那麼喚醒這一步驟在enqueueMessage.
那麼我們來看看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) {
(1) 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;
(2) if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
(3) } 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.
(4) if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
我們來看看3處重要代碼。
- 若當前msg任務停止,則會拋出異常再回收資源。
- 我們可以看到needWake 和mMessages賦予了p,主要就是判斷當前的mMessages是否爲null,null就表示這沒有可執行的Message;當前需要添加的Message的執行時間早於mMessages,需要插入到隊列中,然後將當前線程狀態賦予needWake。
- 這一塊表示異步的msg,異步這一塊不作講解,後面會專門開一篇說異步。
- 最後根據當前線程是否休眠,來調用netiveWake去喚醒線程。
可以總結一下
- 若當前無可執行的msg,則線程會去休眠,此時mBlocked=true
- 若當前執行的msg的時間未到,則會記錄下時間差nextPollTimeoutMillis,並調用nativePollOnce方法讓線程休眠,此時mBlocked=true.
- 若在線程休眠的時候,插入新的msg,則會去喚醒線程,重新排列msg的順序,去執行重新排列後下一個該執行的msg.
通過上訴總結,我們可以回答面試中的一種類似的題目.
1.爲什麼sendMessage加了延遲,線程不會堵塞?
2.因爲當前可執行msg時間未到,系統進行休眠,那麼插入新的msg如何操作?
這些類似的問題都可以從休眠堵塞,以及喚醒這一塊解釋即可.
爲什麼在Activity中的Handler直接new,而子線程中會報錯且提示需要looper.prepare?
5240 Looper.prepareMainLooper();
5241 //創建ActivityThread 對象
5242 ActivityThread thread = new ActivityThread();
5243 thread.attach(false);
5244
5245 if (sMainThreadHandler == null) {
5246 sMainThreadHandler = thread.getHandler();
5247 }
5248
5249 if (false) {
5250 Looper.myLooper().setMessageLogging(new
5251 LogPrinter(Log.DEBUG, "ActivityThread"));
5252 }
5253
5254 Looper.loop();
上述代碼在ActivityThread.java#main方法中,main方法是整個APP初始化的入口,可以看到它已經幫我們做了Looper.prepare 和 Looper.loop,主要是在主線程中,handler直接new即可,不需要再Looper.prepare 和 Looper.loop
在消息機制中,那麼多方法內進行死循環,爲什麼系統不會卡死?
這個問題得要從ActivityThread.java開始說起,整一個APP的初始化在ActivityThread的main方法執行,在一個線程的生命在代碼執行完後結束,那麼如果執行完了這不就意味着這個線程就會消失,那麼在main方法中不就意味着APP結束?所以就需要一個死循環或者是像MessageQueue中的線程休眠喚醒這樣的流程,來保存整個main線程長久的生命週期。從而在MessageQueue的中對線程休眠和喚醒的流程。當然整一個app也不止一個消息機制,會有很多機制,所以會通過創建線程去執行。
下集預告
OkHttp的源碼解讀