說到消息機制,做爲一名 Android 開發者必定先想到的是 Handler。Handler 就是 Android 消息機制的上層接口,咱們可用經過 Handler 輕鬆的在不一樣的線程中切換任務,但 Handler 的實現還有兩個很重要的概念 MessageQueue
和 Looper
。java
MessageQueue 的翻譯是消息隊列,它的內部採用了單鏈表的結構存儲 Handler 對象發送的消息。android
Looper 的做用是不斷地查詢 MessageQueue 中是否有消息,若是 Looper 發現 MessageQueue 中存入了新的消息,它就會去處理這條消息,若是沒有新消息,Looper 就會以無限循環的方式去查詢 MessageQueue 中是否有新消息。數組
(1)安排未來某個時間點執行的 Message
和 Runnables
; (2)在不一樣於當前的線程上執行的操做;安全
在 Android 開發中,默認子線程是不能夠更新 UI 的,這一點能夠從 View 的最高層級 ViewRootImpl 類中找到答案bash
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
}
}
複製代碼
ViewRootImpl 類中的 checkThread
方法會在更新 UI 前被執行,若是當前線程不是主線程,就會拋出 Only the original thread that created a view hierarchy can touch its views.
的異常app
一般,咱們在主線程中建立 Handler 的寫法以下:異步
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
複製代碼
但這樣寫,系統會這樣提示:ide
This Handler class should be static or leaks might occur (anonymous android.os.Handler)
這個Handler類應該是靜態的,不然可能會發生泄漏
複製代碼
出現這個警告但緣由是,Handler 在 Activity 中做爲一個匿名內部類來定義,它的內部持有來 Activity 的實例。當 Activity 被用戶關閉時,由於 Handler 持有了 Activity 的引用,就形成了 Activity 沒法被回收,從而致使了內存泄漏。oop
所以,在這裏推薦一種更加安全的寫法:post
private static class MyHandler extends Handler{
private WeakReference<Activity> weakReference;
public MyHandler(Activity activity){
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
Toast.makeText(weakReference.get(),Thread.currentThread().getName(),Toast.LENGTH_SHORT).show();
break;
}
}
}
private MyHandler handler = new MyHandler(this);
複製代碼
經過靜態內部類的方式實現一個 Handler,此時內部類並不持有外部類對象的應用,須要在內部類的構造方法內增長一個外部類(Activity)的弱應用。這樣,即便 Activity 被關閉,Activity 也能順利被回收。
onCreate() 中的代碼以下:
btn_0 = findViewById(R.id.btn_0);
btn_0.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
@Override
public void run() {
super.run();
Message message = Message.obtain();
message.what = 0;
handler.sendMessage(message);
}
}.start();
}
});
複製代碼
這時候點擊按鈕的運行效果以下:
在官方文檔中 Handler 的主要做用是在不一樣於當前線程的線程中執行操做,那麼如何用 Handler 解決兩個子線程之間的通訊呢?
請看代碼:
btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
@Override
public void run() {
super.run();
Looper.prepare();
handler = new MyHandler(MainActivity.this);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Looper.loop();
}
}.start();
new Thread(){
@Override
public void run() {
super.run();
Message message = Message.obtain();
message.what = 0;
handler.sendMessage(message);
}
}.start();
}
});
複製代碼
此時點擊按鈕:
可見當前的處理線程已經變成了子線程。
若是細心的觀察代碼,能夠看到在子線程中建立 Handler 的時候調用了 Looper.prepare()
和 Looper.loop()
兩個方法。這兩句代碼有什麼用呢?
咱們暫時能夠把 Looper 理解爲消息的管理者,它負責從 MessageQueue 中提取出消息,傳遞給 Handler 進行處理,每個 Handler 都必需要有一個 Looper,在 Handler 建立的時候,它會自動使用當前線程的 Looper,而 Looper.prepare()
的做用就是爲當前線程準備一個 Looper,Looper.loop()
的做用是開始查找當前 MessageQueue 中是否有了新的消息。
這就是 Handler 工做的第一步 :
由於這裏主要講 Handler 的工做流程,建立 Looper 的具體過程放到文章的下面講解。咱們只要知道 Looper.prepare()
爲當前的線程建立了一個 Looper 對象便可。
可是,在主線程中建立 Handler 的時候,咱們並無看到 Looper.prepare()
的執行,這是由於在 UI 線程,即 ActivityThread 的建立過程當中,Looper 已經被建立好了。
咱們能夠在 ActivityThread 的 main() 方法中看到這樣一句代碼:
Looper.prepareMainLooper();
複製代碼
這個方法內部也調用了 Looper.prepare()
爲 UI 線程建立了一個 Looper。
sendMessageAtTime()
方法發送 Message爲何是 sendMessageAtTime
?不是還有 sendMessage()
,sendEmptyMessage()
,sendEmptyMessageDelayed()
,sendEmptyMessageAtTime()
,sendMessageDelayed()
這麼多方法嗎?
經過閱讀這些方法的源碼能夠發現,這些方法最終調用的都是 sendMessageAtTime()
。
其次還有 post()
,postAtTime()
,postDelayed()
方法最終調用的也都是 sendMessageAtTime()
方法,只是多了一步調用 getPostMessage(Runnable r, Object token)
將 Runnable 封裝爲一個 Message 對象的 callback 裏。
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;
}
複製代碼
那麼 sendMessageAtTime()
裏的具體操做是什麼呢?咱們去源碼裏一探究竟
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 先獲取當前 Handler 中的 MessageQueue,mQueue 在 Looper 的構造方法中進行初始化。
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// queue 不爲空,則執行 Handler.java 裏的另外一個 enqueueMessage() 方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 指定 msg 的 Target 對象爲當前的 Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
Handler 中的 enqueueMessage() ,最終會調用 MessageQueue.java 中的 enqueueMessage() 方法。
以後,Message 對象最終傳遞到 MessageQueue 即消息隊列裏中,在消息隊列裏的具體處理邏輯在文章的 MessageQueue 工做原理 部分會具體解釋。
在第二步將消息插入消息隊列後,Looper 就開始遍歷消息隊列,找到新的消息,再通知 Handler 去執行這條消息,調用的就是 Handler 的 dispatchMessage()
方法。
public void dispatchMessage(Message msg) {
// msg 的 callback 對象就是一個 Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
// 檢查 mCallback 是否爲空,不爲空就執行它內部定義的 handleMessage() 方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 若是 mCallback 爲空,就執行在實例化 Handler 過程當中咱們本身定義的 handleMessage() 方法中的內容
handleMessage(msg);
}
}
複製代碼
dispatchMessage()
方法首先會檢查 Message 的 Callback 對象是否爲空,callback 就是經過 post() 方法傳遞的 Runnable 對象,若是 callback 不爲空,就去執行 handleCallback() 方法。
handleCallback() 方法的實現也很簡單,它在內部執行了 Runnable 的 run()
方法
private static void handleCallback(Message message) {
message.callback.run();
}
複製代碼
若是 callback 對象爲空,就檢查 mCallback 是否爲空,不爲空就執行它的定義的 handleMessage() 方法,若沒有 mCallback,最終將直接執行咱們在繼承 Handler 時本身定義的 handleMessage() 方法中的代碼。
Callback
是 Handler 中定義的的一個接口,它的代碼以下:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */
public interface Callback {
/** * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */
public boolean handleMessage(Message msg);
}
複製代碼
若是使用 Callback 接口的話,咱們能夠直接實例化一個 Handler 而不用去實現一個 Handler 的子類,
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
複製代碼
咱們從上一部分的 MessageQueue.java 中的 enqueueMessage() 方法開始入手。
代碼量有點多,要耐心看哦!
boolean enqueueMessage(Message msg, long when) {
// 檢查當前 msg 的 target 是否爲空
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// msg 若是正在被執行,就拋出異常
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
// 在 quit() 方法中,mQuitting 會被設置爲 true
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 正在執行
msg.markInUse();
// 設置 msg 的 when 爲傳進來的 when 參數,when 是 Message 想要被執行的時間
msg.when = when;
// 獲得當前消息隊列的頭部消息
Message p = mMessages;
boolean needWake;
// 當前消息隊列爲空,新消息的觸發時間爲 0,或者新消息的觸發時間早於消息中第一條消息的觸發時間
// 則將新消息插入到隊列的頭部,做爲當前消息隊列的第一條消息
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 {
// 將新消息插入到當前消息隊列當中,(不是頭部)
// 一般咱們沒必要喚醒事件隊列,
// 除非隊列頭部有消息障礙,而且消息是隊列中最先的異步消息。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 開始循環便利消息隊列,比較新消息和隊列中消息的 when(觸發事件)的值,將新消息插入到適當位置
for (;;) {
// 循環第一次遍歷時,將當前隊列中的頭部消息賦值給 prev
prev = p;
// p 指向隊列中的第二個消息
p = p.next;
// 若是下一個消息爲空,或者新消息的觸發時間早於下一個消息,找到了要插入的位置,退出循環
if (p == null || when < p.when) {
break;
}
// needWake 爲 true,而且 下一條消息是異步的,則不須要喚醒。
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
// 將新消息插入到 p 以前,頭消息以後。
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// 若是須要喚醒,調用 nativeWake 方法去喚醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
複製代碼
執行完 enqueueMassage 方法,咱們新發送的 Message 就成功的插入了消息隊列當中。 可是除了插入新消息,咱們還須要從消息隊列中讀取消息,這又要怎麼作呢?
Message next() {
// 若是消息循環已退出,而且被丟棄,則返回空。
// 這個將在應用重啓一個 looper 時發生
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
// 記錄空閒時處理的 IdlerHandler 數量,只在第一次迭代時爲 -1
// IdleHandler 只在隊列爲空 或者 是頭部消息時執行
int pendingIdleHandlerCount = -1;
// native 層使用的變量,設置的阻塞超時時長,0 爲不阻塞,-1 爲阻塞
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
// 嘗試檢索下一條消息。 若是找到則返回。
synchronized (this) {
// 獲取系統從開機到如今到時間
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
// 將隊列中到頭部消息賦值給 msg
Message msg = mMessages;
if (msg != null && msg.target == null) {
// msg 不爲空,可是這個 msg 沒有 handler,則這個 msg 爲柵欄
// 開始遍歷,指到獲取第一個異步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 若是當前時間不到 msg 的觸發時間,則計算時間差,設置阻塞超時時長
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 的觸發時間,則獲取消息並返回
mBlocked = false;
// 若是當前的 msg 不是頭部消息,則上一條消息的 next 指向 msg 的 next
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
// 當前 msg 爲頭部消息,則將下一個 msg 設置爲頭部消息
mMessages = msg.next;
}
// msg 的下一個 Message 對象置空,表示從消息隊列中取出來了這條 msg
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
// 標記 msg 正在使用
msg.markInUse();
return msg;
}
} else {
// 若是沒有消息,則設置阻塞時長爲 -1,直到被喚醒
nextPollTimeoutMillis = -1;
}
// 全部的消息都被處理後,判斷是否退出,並返回 null。
if (mQuitting) {
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.
// 第一次循環時,消息隊列爲空,或 當前時間未到消息的觸發時間,獲取 IdleHandler 的數量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// pendingIdleHandlerCount 的數量爲 0 時,線程會繼續堵塞
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
// 判斷當前空閒時處理任務的handler是不是爲空,若是爲空,就實例化出新的對象
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 運行 IdleHandler,只有第一次循環時纔會運行
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
// 釋放 IdleHandler 的引用
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
// 執行 IdleHandler 的方法
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 重置 IdleHandler 的數量爲 0,確保不會重複運行它們
pendingIdleHandlerCount = 0;
// 在執行 IdleHandler 時,一個新的消息可能插入或消息隊列中的消息到了觸發時間
// 因此將 nextPollTimeoutMillis 設爲 0,表示不須要阻塞,從新檢查消息隊列。
nextPollTimeoutMillis = 0;
}
}
複製代碼
至此,MessageQueue 的兩個最重要的方法已經分析完了,下面來看 Looper 如何循環地從消息隊列中取出消息。
在講 Looper 以前,須要先理解 ThreadLocal 的工做原理
ThreadLocal 是一個線程內存儲數據的類,當不一樣的線程去訪問同一個 ThreadLocal 對象時,得到的值都是不同的,下面用一段代碼來證實
private ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
btn_1 = findViewById(R.id.btn_1);
btn_1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(){
@Override
public void run() {
super.run();
mThreadLocal.set("Thread_A");
Log.d("ThreadLocalValue",mThreadLocal.get());
}
}.start();
new Thread(){
@Override
public void run() {
super.run();
mThreadLocal.set("Thread_B");
Log.d("ThreadLocalValue",mThreadLocal.get());
}
}.start();
}
);
複製代碼
我在兩個線程中分別存入在 mThreadLocal 中存入了不一樣的值,而後在控制檯輸出它們的內容
可見不一樣線程訪問同一個 ThreadLocal 對象獲得的值也是不同的。
ThreadLocal 實現這種特性的緣由也很簡單,下面來看它內部的 set 方法:
public void set(T value) {
// 獲取當前線程 t
Thread t = Thread.currentThread();
// 根據當前線程 t,獲取當前線程的 ThreadLocalMap 對象
ThreadLocalMap map = getMap(t);
if (map != null)
// map 不爲空,調用 ThreadLocalMap 的 set() 方法。
map.set(this, value);
else
// map 爲空,則爲當前線程建立一個新的 ThreadLocalMap 對象
createMap(t, value);
}
複製代碼
在 set 方法中,先獲取當前線程,而後獲取當前線程的 ThreadLocalMap 對象。getMap() 的 和 createMap() 的實現以下:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
複製代碼
那麼 ThreadLocalMap 又是什麼呢,這裏是它的一部分源碼:
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 初始的 table 容量
private static final int INITIAL_CAPACITY = 16;
// Entry 數組用於存儲數據
private Entry[] table;
// table 的大小
private int size = 0;
// 負載因子,用於擴容
private int threshold; // Default to 0
// 設置負載因子爲固然容量大小的 2 / 3
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
// 初始化 Entry 數組
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}
複製代碼
能夠將 ThreadLocalMap 看成一個哈希表,它的內部用 Entry 存儲相應的數據。
在 Thread 的屬性中有 ThreadLocal.ThreadLocalMap threadLocals = null;
,因此每個線程內部,都持有一個 ThreadLocalMap 對象,系統才能夠經過 getMap()
方法獲取當前線程的 ThreadLocalMap 對象。
在 ThreadLocal 中調用 set 方法,實際上會調用 ThreadLocalMap 中的 set 方法,源碼以下:
// ThreadLocalMap 的 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.
// 首先獲取當前 ThreadLocal 對象的 table 屬性,table 一個 Entry 的數組
// Entry 至關於一個 HashMap,存儲了當前 ThreadLocal 對象和 Object 類型的 value 對象
Entry[] tab = table;
int len = tab.length;
// 計算出存儲的位置
int i = key.threadLocalHashCode & (len-1);
// 遍歷 tab
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
// 若是 tab 中已經存在了相同的 key 值,就覆蓋它原有的 value
if (k == key) {
e.value = value;
return;
}
// 若是 當前 entrt 的 key 爲 null,調用 replaceStaleEntry 方法清楚全部 key 爲 null 的數據
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// 都不知足,就新建一個 Entry 對象
tab[i] = new Entry(key, value);
int sz = ++size;
// ThreadLocalMap 的容量到達閥值後擴容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
複製代碼
ThreadLocal 中的 get() 方法和 set() 方法同樣,都是對 Thread 中對 ThreadLocalMap 進行操做
public T get() {
// 獲取當前線程
Thread t = Thread.currentThread();
// 獲取當前線程的 ThreadLocalMap 對象
ThreadLocalMap map = getMap(t);
if (map != null) {
// 獲取 ThreadLocalMap 中對應當前線程的 Entry 對象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
// 將 Entry 對象中的 value 取出來
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
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);
}
複製代碼
那麼 ThreadLocal 和 Looper 有什麼關係呢?咱們知道每個線程都有本身的 Looper,Looper 的做用域就是當前的線程,Android 系統中便經過 ThreadLocal 對象來存儲不一樣線程中的 Looper。
Looper 中 prepare() 方法爲當前線程建立一個 Looper 對象,咱們看一下它的實現:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 將 Looper 對象保存到當前線程的 ThreadLocalMap 當中
sThreadLocal.set(new Looper(quitAllowed));
}
複製代碼
這裏再看一下 Looper 的構造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製代碼
能夠看到在一個 Looper 中建立了一個 MessageQueue,這裏咱們就能夠搞清楚 Handler、Looper 和 MessageQueue 的對應關係了:
每一個線程都有一個 Looper 對象,在 Looper 對象的初始化過程當中,會爲當前線程建立一個 MessageQueue,而一個線程中能夠有多個 Handler。
prepare() 調用後,就是調用 loop() 方法:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */
public static void loop() {
// 經過 Thread Local 獲取當前線程的 Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 獲取當前 Looper 對象的 MessageQueue
final MessageQueue queue = me.mQueue;
// 清空遠程調用端進程的身份,確保此線程的身份是本地進程的身份,並跟蹤該身份令牌
// 這裏主要用於保證消息處理是發生在當前 Looper 所在的線程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
// 取出來下一條消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 用 logging 打印日誌,默認爲 null,可經過 setMessageLogging() 方法來指定
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// 開始跟蹤,並寫入跟蹤消息,用於 debug 功能
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
...
...
try {
// // 經過 Handler 分發消息
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
// 中止跟蹤
Trace.traceEnd(traceTag);
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 確保在分發消息的過程當中線程的身份沒有改變
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();
}
}
複製代碼
能夠看到 loop() 方法就是不停的遍歷消息隊列中的消息,當發現有新的消息時,便調用 Handler 的 dispatchMessage()
方法。
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;
}
}
複製代碼
getMainLooper() 方法用於返回當前 UI 線程的 Looper,UI 線程的 Looper 在 ActivityThread 的創建時經過調用 prepareMainLooper()
方法建立。
在子線程中,若是手動爲其建立了Looper,那麼在全部消息處理完成以後應該調用 quit() 方法終止消息循環,否則 Looper 就會一直處於等待狀態。
public void quitSafely() {
mQueue.quit(true);
}
public void quit() {
mQueue.quit(false);
}
複製代碼
能夠看到這兩個方法都調用了 MessageQueue 中都 quit(boolean safe) 方法,quitSafely 的參數爲 true,quit 的參數爲 false。
void quit(boolean safe) {
// 主線程不退出消息循環
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
// 若是已經退出了,直接 return
if (mQuitting) {
return;
}
// 標記爲已經退出
mQuitting = true;
// 若是 safe 的值爲 true,執行完當前的消息後退出消息循環
if (safe) {
removeAllFutureMessagesLocked();
} else {
// 直接退出消息循環
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
複製代碼
quitSafely()
會等待當前消息執行完畢後退出消息循環,而 quit()
方法會直接退出消息循環。
private void removeAllMessagesLocked() {
// 獲取當前 MessageQueue 的頭部消息
Message p = mMessages;
while (p != null) {
// 循環遍歷全部的 Message
Message n = p.next;
// 回收消息,並把消息放入消息池
p.recycleUnchecked();
p = n;
}
// 將頭部消息置爲空
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
// 獲取系統從開機到如今到時間
final long now = SystemClock.uptimeMillis();
// 將當前的頭部消息賦值給 p
Message p = mMessages;
if (p != null) {
if (p.when > now) {
// 若是當前頭部消息將要執行的時間大於系統開機到如今的時間,則執行 removeAllMessagesLocked() 方法
// 清空 MessageQueue 隊列
removeAllMessagesLocked();
} else {
Message n;
// 遍歷當前的 MessageQueue,直到某個消息的執行時間小於 now 值(即這個消息正在執行)
// 將這個消息的 next 賦值爲 null
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
// 回收不會被執行的 Message
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
複製代碼
終於講完了,但願你們能經過個人文章,完全理解 Handler 的機制,但個人能力有限,若是存在錯誤的地方,還請指出。
零碎的東西不少,爲了方便你們記憶,我把上面的內容作成了思惟導圖,須要的朋友能夠保存下來,偶爾看一下,幫助本身記憶。
歡迎關注本文做者:
掃碼關注並回復「乾貨」,獲取我整理的千G Android、iOS、JavaWeb、大數據、人工智能等學習資源。