摘要:什么是消息機(jī)制說(shuō)到消息機(jī)制,作為一名開(kāi)發(fā)者一定先想到的是。但是,在主線程中創(chuàng)建的時(shí)候,我們并沒(méi)有看到的執(zhí)行,這是因?yàn)樵诰€程,即的創(chuàng)建過(guò)程中,已經(jīng)被創(chuàng)建好了。將新消息插入到之前,頭消息之后。
1. 什么是消息機(jī)制
說(shuō)到消息機(jī)制,作為一名 Android 開(kāi)發(fā)者一定先想到的是 Handler。Handler 就是 Android 消息機(jī)制的上層接口,我們可用通過(guò) Handler 輕松的在不同的線程中切換任務(wù),但 Handler 的實(shí)現(xiàn)還有兩個(gè)很重要的概念 MessageQueue 和 Looper。
MessageQueue 的翻譯是消息隊(duì)列,它的內(nèi)部采用了單鏈表的結(jié)構(gòu)存儲(chǔ) Handler 對(duì)象發(fā)送的消息。
Looper 的作用是不斷地查詢(xún) MessageQueue 中是否有消息,如果 Looper 發(fā)現(xiàn) MessageQueue 中存入了新的消息,它就會(huì)去處理這條消息,如果沒(méi)有新消息,Looper 就會(huì)以無(wú)限循環(huán)的方式去查詢(xún) MessageQueue 中是否有新消息。
2. 為什么要有 Handler 2.1)官方文檔中 Handler 的主要作用(1)安排將來(lái)某個(gè)時(shí)間點(diǎn)執(zhí)行的 Message 和 Runnables;
(2)在不同于當(dāng)前的線程上執(zhí)行的操作;
在 Android 開(kāi)發(fā)中,默認(rèn)子線程是不可以更新 UI 的,這一點(diǎn)可以從 View 的最高層級(jí) ViewRootImpl 類(lèi)中找到答案
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views."); } }
ViewRootImpl 類(lèi)中的 checkThread 方法會(huì)在更新 UI 前被執(zhí)行,如果當(dāng)前線程不是主線程,就會(huì)拋出 Only the original thread that created a view hierarchy can touch its views. 的異常
2.3)那么 Android 為什么要設(shè)計(jì)為只能在主線程中更新 UI 呢?Android 在子線程中更新 UI 是不安全的,如果多個(gè)子線程同時(shí)修改一個(gè)控件的數(shù)據(jù),后果是不可控的
如果給 UI 更新機(jī)制加鎖,會(huì)降低 UI 的訪問(wèn)效率,并且可能阻塞某些線程的執(zhí)行
3. Handler 的用法 3.1)在主線程中創(chuàng)建 Handler通常,我們?cè)谥骶€程中創(chuàng)建 Handler 的寫(xiě)法如下:
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } };
但這樣寫(xiě),系統(tǒng)會(huì)這樣提示:
This Handler class should be static or leaks might occur (anonymous android.os.Handler) 這個(gè)Handler類(lèi)應(yīng)該是靜態(tài)的,否則可能會(huì)發(fā)生泄漏
出現(xiàn)這個(gè)警告但原因是,Handler 在 Activity 中作為一個(gè)匿名內(nèi)部類(lèi)來(lái)定義,它的內(nèi)部持有來(lái) Activity 的實(shí)例。當(dāng) Activity 被用戶(hù)關(guān)閉時(shí),因?yàn)?Handler 持有了 Activity 的引用,就造成了 Activity 無(wú)法被回收,從而導(dǎo)致了內(nèi)存泄漏。
因此,在這里推薦一種更加安全的寫(xiě)法:
private static class MyHandler extends Handler{ private WeakReferenceweakReference; 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);
通過(guò)靜態(tài)內(nèi)部類(lèi)的方式實(shí)現(xiàn)一個(gè) Handler,此時(shí)內(nèi)部類(lèi)并不持有外部類(lèi)對(duì)象的應(yīng)用,需要在內(nèi)部類(lèi)的構(gòu)造方法內(nèi)增加一個(gè)外部類(lèi)(Activity)的弱應(yīng)用。這樣,即使 Activity 被關(guān)閉,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(); } });
這時(shí)候點(diǎn)擊按鈕的運(yùn)行效果如下:
在官方文檔中 Handler 的主要作用是在不同于當(dāng)前線程的線程中執(zhí)行操作,那么如何用 Handler 解決兩個(gè)子線程之間的通信呢?
請(qǐng)看代碼:
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(); } });
此時(shí)點(diǎn)擊按鈕:
可見(jiàn)當(dāng)前的處理線程已經(jīng)變成了子線程。
4. Handler 工作原理如果細(xì)心的觀察代碼,可以看到在子線程中創(chuàng)建 Handler 的時(shí)候調(diào)用了 Looper.prepare() 和 Looper.loop() 兩個(gè)方法。這兩句代碼有什么用呢?
我們暫時(shí)可以把 Looper 理解為消息的管理者,它負(fù)責(zé)從 MessageQueue 中提取出消息,傳遞給 Handler 進(jìn)行處理,每一個(gè) Handler 都必須要有一個(gè) Looper,在 Handler 創(chuàng)建的時(shí)候,它會(huì)自動(dòng)使用當(dāng)前線程的 Looper,而 Looper.prepare() 的作用就是為當(dāng)前線程準(zhǔn)備一個(gè) Looper,Looper.loop() 的作用是開(kāi)始查找當(dāng)前 MessageQueue 中是否有了新的消息。
這就是 Handler 工作的第一步 :
4.1)采用當(dāng)前線程的 Looper 創(chuàng)建 Handler因?yàn)檫@里主要講 Handler 的工作流程,創(chuàng)建 Looper 的具體過(guò)程放到文章的下面講解。我們只要知道
Looper.prepare() 為當(dāng)前的線程創(chuàng)建了一個(gè) Looper 對(duì)象即可。
但是,在主線程中創(chuàng)建 Handler 的時(shí)候,我們并沒(méi)有看到 Looper.prepare() 的執(zhí)行,這是因?yàn)樵?UI 線程,即 ActivityThread 的創(chuàng)建過(guò)程中,Looper 已經(jīng)被創(chuàng)建好了。
我們可以在 ActivityThread 的 main() 方法中看到這樣一句代碼:
Looper.prepareMainLooper();
這個(gè)方法內(nèi)部也調(diào)用了 Looper.prepare() 為 UI 線程創(chuàng)建了一個(gè) Looper。
4.2)通過(guò) Handler 的 sendMessageAtTime() 方法發(fā)送 Message為什么是 sendMessageAtTime?不是還有 sendMessage(),sendEmptyMessage(),sendEmptyMessageDelayed(),sendEmptyMessageAtTime(),sendMessageDelayed() 這么多方法嗎?
通過(guò)閱讀這些方法的源碼可以發(fā)現(xiàn),這些方法最終調(diào)用的都是 sendMessageAtTime()。
其次還有 post(),postAtTime(),postDelayed() 方法最終調(diào)用的也都是 sendMessageAtTime() 方法,只是多了一步調(diào)用 getPostMessage(Runnable r, Object token) 將 Runnable 封裝為一個(gè) Message 對(duì)象的 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() 里的具體操作是什么呢?我們?nèi)ピ创a里一探究竟
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { // 先獲取當(dāng)前 Handler 中的 MessageQueue,mQueue 在 Looper 的構(gòu)造方法中進(jìn)行初始化。 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 不為空,則執(zhí)行 Handler.java 里的另一個(gè) enqueueMessage() 方法 return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 指定 msg 的 Target 對(duì)象為當(dāng)前的 Handler msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
Handler 中的 enqueueMessage() ,最終會(huì)調(diào)用 MessageQueue.java 中的 enqueueMessage() 方法。
之后,Message 對(duì)象最終傳遞到 MessageQueue 即消息隊(duì)列里中,在消息隊(duì)列里的具體處理邏輯在文章的 MessageQueue 工作原理 部分會(huì)具體解釋。
4.3)Looper 處理消息后調(diào)用 Handler 的 dispatchMessage() 方法在第二步將消息插入消息隊(duì)列后,Looper 就開(kāi)始遍歷消息隊(duì)列,找到新的消息,再通知 Handler 去執(zhí)行這條消息,調(diào)用的就是 Handler 的 dispatchMessage() 方法。
public void dispatchMessage(Message msg) { // msg 的 callback 對(duì)象就是一個(gè) Runnable if (msg.callback != null) { handleCallback(msg); } else { // 檢查 mCallback 是否為空,不為空就執(zhí)行它內(nèi)部定義的 handleMessage() 方法 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 如果 mCallback 為空,就執(zhí)行在實(shí)例化 Handler 過(guò)程中我們自己定義的 handleMessage() 方法中的內(nèi)容 handleMessage(msg); } }
dispatchMessage() 方法首先會(huì)檢查 Message 的 Callback 對(duì)象是否為空,callback 就是通過(guò) post() 方法傳遞的 Runnable 對(duì)象,如果 callback 不為空,就去執(zhí)行 handleCallback() 方法。
handleCallback() 方法的實(shí)現(xiàn)也很簡(jiǎn)單,它在內(nèi)部執(zhí)行了 Runnable 的 run() 方法
private static void handleCallback(Message message) { message.callback.run(); }
如果 callback 對(duì)象為空,就檢查 mCallback 是否為空,不為空就執(zhí)行它的定義的 handleMessage() 方法,若沒(méi)有 mCallback,最終將直接執(zhí)行我們?cè)诶^承 Handler 時(shí)自己定義的 handleMessage() 方法中的代碼。
Callback 是 Handler 中定義的的一個(gè)接口,它的代碼如下:
/** * 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 接口的話,我們可以直接實(shí)例化一個(gè) Handler 而不用去實(shí)現(xiàn)一個(gè) Handler 的子類(lèi),
private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } });5. MessageQueue 工作原理
我們從上一部分的 MessageQueue.java 中的 enqueueMessage() 方法開(kāi)始入手。
5.1)enqueueMessage()代碼量有點(diǎn)多,要耐心看哦!
boolean enqueueMessage(Message msg, long when) { // 檢查當(dāng)前 msg 的 target 是否為空 if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } // msg 如果正在被執(zhí)行,就拋出異常 if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { // 在 quit() 方法中,mQuitting 會(huì)被設(shè)置為 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; } // 標(biāo)記當(dāng)前的 msg 正在執(zhí)行 msg.markInUse(); // 設(shè)置 msg 的 when 為傳進(jìn)來(lái)的 when 參數(shù),when 是 Message 想要被執(zhí)行的時(shí)間 msg.when = when; // 得到當(dāng)前消息隊(duì)列的頭部消息 Message p = mMessages; boolean needWake; // 當(dāng)前消息隊(duì)列為空,新消息的觸發(fā)時(shí)間為 0,或者新消息的觸發(fā)時(shí)間早于消息中第一條消息的觸發(fā)時(shí)間 // 則將新消息插入到隊(duì)列的頭部,作為當(dāng)前消息隊(duì)列的第一條消息 if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. // 將當(dāng)前消息的下一條消息指向頭部消息 msg.next = p; // 頭部消息修改為當(dāng)前消息 mMessages = msg; // 當(dāng)阻塞時(shí),需要喚醒 needWake = mBlocked; } else { // 將新消息插入到當(dāng)前消息隊(duì)列當(dāng)中,(不是頭部) // 通常我們不必喚醒事件隊(duì)列, // 除非隊(duì)列頭部有消息障礙,并且消息是隊(duì)列中最早的異步消息。 needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; // 開(kāi)始循環(huán)便利消息隊(duì)列,比較新消息和隊(duì)列中消息的 when(觸發(fā)事件)的值,將新消息插入到適當(dāng)位置 for (;;) { // 循環(huán)第一次遍歷時(shí),將當(dāng)前隊(duì)列中的頭部消息賦值給 prev prev = p; // p 指向隊(duì)列中的第二個(gè)消息 p = p.next; // 如果下一個(gè)消息為空,或者新消息的觸發(fā)時(shí)間早于下一個(gè)消息,找到了要插入的位置,退出循環(huán) 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; } // 如果需要喚醒,調(diào)用 nativeWake 方法去喚醒 if (needWake) { nativeWake(mPtr); } } return true; }
執(zhí)行完 enqueueMassage 方法,我們新發(fā)送的 Message 就成功的插入了消息隊(duì)列當(dāng)中。
但是除了插入新消息,我們還需要從消息隊(duì)列中讀取消息,這又要怎么做呢?
Message next() { // 如果消息循環(huán)已退出,并且被丟棄,則返回空。 // 這個(gè)將在應(yīng)用重啟一個(gè) looper 時(shí)發(fā)生 final long ptr = mPtr; if (ptr == 0) { return null; } // 記錄空閑時(shí)處理的 IdlerHandler 數(shù)量,只在第一次迭代時(shí)為 -1 // IdleHandler 只在隊(duì)列為空 或者 是頭部消息時(shí)執(zhí)行 int pendingIdleHandlerCount = -1; // native 層使用的變量,設(shè)置的阻塞超時(shí)時(shí)長(zhǎng),0 為不阻塞,-1 為阻塞 int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); // 嘗試檢索下一條消息。 如果找到則返回。 synchronized (this) { // 獲取系統(tǒng)從開(kāi)機(jī)到現(xiàn)在到時(shí)間 final long now = SystemClock.uptimeMillis(); Message prevMsg = null; // 將隊(duì)列中到頭部消息賦值給 msg Message msg = mMessages; if (msg != null && msg.target == null) { // msg 不為空,但是這個(gè) msg 沒(méi)有 handler,則這個(gè) msg 為柵欄 // 開(kāi)始遍歷,指到獲取第一個(gè)異步消息 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { // 如果當(dāng)前時(shí)間不到 msg 的觸發(fā)時(shí)間,則計(jì)算時(shí)間差,設(shè)置阻塞超時(shí)時(shí)長(zhǎng) 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 { // 當(dāng)前時(shí)間到了 msg 的觸發(fā)時(shí)間,則獲取消息并返回 mBlocked = false; // 如果當(dāng)前的 msg 不是頭部消息,則上一條消息的 next 指向 msg 的 next if (prevMsg != null) { prevMsg.next = msg.next; } else { // 當(dāng)前 msg 為頭部消息,則將下一個(gè) msg 設(shè)置為頭部消息 mMessages = msg.next; } // msg 的下一個(gè) Message 對(duì)象置空,表示從消息隊(duì)列中取出來(lái)了這條 msg msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); // 標(biāo)記 msg 正在使用 msg.markInUse(); return msg; } } else { // 如果沒(méi)有消息,則設(shè)置阻塞時(shí)長(zhǎng)為 -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. // 第一次循環(huán)時(shí),消息隊(duì)列為空,或 當(dāng)前時(shí)間未到消息的觸發(fā)時(shí)間,獲取 IdleHandler 的數(shù)量 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } // pendingIdleHandlerCount 的數(shù)量為 0 時(shí),線程會(huì)繼續(xù)堵塞 if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } // 判斷當(dāng)前空閑時(shí)處理任務(wù)的handler是否是為空,如果為空,就實(shí)例化出新的對(duì)象 if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // 運(yùn)行 IdleHandler,只有第一次循環(huán)時(shí)才會(huì)運(yùn)行 for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; // 釋放 IdleHandler 的引用 mPendingIdleHandlers[i] = null; boolean keep = false; try { // 執(zhí)行 IdleHandler 的方法 keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // 重置 IdleHandler 的數(shù)量為 0,確保不會(huì)重復(fù)運(yùn)行它們 pendingIdleHandlerCount = 0; // 在執(zhí)行 IdleHandler 時(shí),一個(gè)新的消息可能插入或消息隊(duì)列中的消息到了觸發(fā)時(shí)間 // 所以將 nextPollTimeoutMillis 設(shè)為 0,表示不需要阻塞,重新檢查消息隊(duì)列。 nextPollTimeoutMillis = 0; } }
至此,MessageQueue 的兩個(gè)最重要的方法已經(jīng)分析完了,下面來(lái)看 Looper 如何循環(huán)地從消息隊(duì)列中取出消息。
6. Looper 工作原理在講 Looper 之前,需要先理解 ThreadLocal 的工作原理
6.1)ThreadLocal 的工作原理ThreadLocal 是一個(gè)線程內(nèi)存儲(chǔ)數(shù)據(jù)的類(lèi),當(dāng)不同的線程去訪問(wèn)同一個(gè) ThreadLocal 對(duì)象時(shí),獲得的值都是不一樣的,下面用一段代碼來(lái)證明
private ThreadLocalmThreadLocal = 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(); } );
我在兩個(gè)線程中分別存入在 mThreadLocal 中存入了不同的值,然后在控制臺(tái)輸出它們的內(nèi)容
可見(jiàn)不同線程訪問(wèn)同一個(gè) ThreadLocal 對(duì)象得到的值也是不一樣的。
ThreadLocal 實(shí)現(xiàn)這種特性的原因也很簡(jiǎn)單,下面來(lái)看它內(nèi)部的 set 方法:
public void set(T value) { // 獲取當(dāng)前線程 t Thread t = Thread.currentThread(); // 根據(jù)當(dāng)前線程 t,獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象 ThreadLocalMap map = getMap(t); if (map != null) // map 不為空,調(diào)用 ThreadLocalMap 的 set() 方法。 map.set(this, value); else // map 為空,則為當(dāng)前線程創(chuàng)建一個(gè)新的 ThreadLocalMap 對(duì)象 createMap(t, value); }
在 set 方法中,先獲取當(dāng)前線程,然后獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象。getMap() 的 和 createMap() 的實(shí)現(xiàn)如下:
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> { /** 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 數(shù)組用于存儲(chǔ)數(shù)據(jù) private Entry[] table; // table 的大小 private int size = 0; // 負(fù)載因子,用于擴(kuò)容 private int threshold; // Default to 0 // 設(shè)置負(fù)載因子為當(dāng)然容量大小的 2 / 3 private void setThreshold(int len) { threshold = len * 2 / 3; } // 初始化 Entry 數(shù)組 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 當(dāng)作一個(gè)哈希表,它的內(nèi)部用 Entry 存儲(chǔ)相應(yīng)的數(shù)據(jù)。
在 Thread 的屬性中有 ThreadLocal.ThreadLocalMap threadLocals = null;,所以每一個(gè)線程內(nèi)部,都持有一個(gè) ThreadLocalMap 對(duì)象,系統(tǒng)才可以通過(guò) getMap() 方法獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象。
在 ThreadLocal 中調(diào)用 set 方法,實(shí)際上會(huì)調(diào)用 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. // 首先獲取當(dāng)前 ThreadLocal 對(duì)象的 table 屬性,table 一個(gè) Entry 的數(shù)組 // Entry 相當(dāng)于一個(gè) HashMap,存儲(chǔ)了當(dāng)前 ThreadLocal 對(duì)象和 Object 類(lèi)型的 value 對(duì)象 Entry[] tab = table; int len = tab.length; // 計(jì)算出存儲(chǔ)的位置 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 中已經(jīng)存在了相同的 key 值,就覆蓋它原有的 value if (k == key) { e.value = value; return; } // 如果 當(dāng)前 entrt 的 key 為 null,調(diào)用 replaceStaleEntry 方法清楚所有 key 為 null 的數(shù)據(jù) if (k == null) { replaceStaleEntry(key, value, i); return; } } // 都不滿足,就新建一個(gè) Entry 對(duì)象 tab[i] = new Entry(key, value); int sz = ++size; // ThreadLocalMap 的容量到達(dá)閥值后擴(kuò)容 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
ThreadLocal 中的 get() 方法和 set() 方法一樣,都是對(duì) Thread 中對(duì) ThreadLocalMap 進(jìn)行操作
public T get() { // 獲取當(dāng)前線程 Thread t = Thread.currentThread(); // 獲取當(dāng)前線程的 ThreadLocalMap 對(duì)象 ThreadLocalMap map = getMap(t); if (map != null) { // 獲取 ThreadLocalMap 中對(duì)應(yīng)當(dāng)前線程的 Entry 對(duì)象 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") // 將 Entry 對(duì)象中的 value 取出來(lái) 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); }6.2)Looper 中的 prepare() 方法
那么 ThreadLocal 和 Looper 有什么關(guān)系呢?我們知道每一個(gè)線程都有自己的 Looper,Looper 的作用域就是當(dāng)前的線程,Android 系統(tǒng)中便通過(guò) ThreadLocal 對(duì)象來(lái)存儲(chǔ)不同線程中的 Looper。
Looper 中 prepare() 方法為當(dāng)前線程創(chuàng)建一個(gè) Looper 對(duì)象,我們看一下它的實(shí)現(xiàn):
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 對(duì)象保存到當(dāng)前線程的 ThreadLocalMap 當(dāng)中 sThreadLocal.set(new Looper(quitAllowed)); }
這里再看一下 Looper 的構(gòu)造方法
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
可以看到在一個(gè) Looper 中創(chuàng)建了一個(gè) MessageQueue,這里我們就可以搞清楚 Handler、Looper 和 MessageQueue 的對(duì)應(yīng)關(guān)系了:
每個(gè)線程都有一個(gè) Looper 對(duì)象,在 Looper 對(duì)象的初始化過(guò)程中,會(huì)為當(dāng)前線程創(chuàng)建一個(gè) MessageQueue,而一個(gè)線程中可以有多個(gè) Handler。
6.3)Looper 中的 loop() 方法:prepare() 調(diào)用后,就是調(diào)用 loop() 方法:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { // 通過(guò) Thread Local 獲取當(dāng)前線程的 Looper final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn"t called on this thread."); } // 獲取當(dāng)前 Looper 對(duì)象的 MessageQueue final MessageQueue queue = me.mQueue; // 清空遠(yuǎn)程調(diào)用端進(jìn)程的身份,確保此線程的身份是本地進(jìn)程的身份,并跟蹤該身份令牌 // 這里主要用于保證消息處理是發(fā)生在當(dāng)前 Looper 所在的線程 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { // 取出來(lái)下一條消息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // 用 logging 打印日志,默認(rèn)為 null,可通過(guò) setMessageLogging() 方法來(lái)指定 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } // 開(kāi)始跟蹤,并寫(xiě)入跟蹤消息,用于 debug 功能 final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } ... ... try { // // 通過(guò) Handler 分發(fā)消息 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); } // 確保在分發(fā)消息的過(guò)程中線程的身份沒(méi)有改變 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() 方法就是不停的遍歷消息隊(duì)列中的消息,當(dāng)發(fā)現(xiàn)有新的消息時(shí),便調(diào)用 Handler 的 dispatchMessage() 方法。
6.4)getMainLooper()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() 方法用于返回當(dāng)前 UI 線程的 Looper,UI 線程的 Looper 在 ActivityThread 的建立時(shí)通過(guò)調(diào)用
prepareMainLooper() 方法創(chuàng)建。
在子線程中,如果手動(dòng)為其創(chuàng)建了Looper,那么在所有消息處理完成之后應(yīng)該調(diào)用 quit() 方法終止消息循環(huán),不然 Looper 就會(huì)一直處于等待狀態(tài)。
public void quitSafely() { mQueue.quit(true); } public void quit() { mQueue.quit(false); }
可以看到這兩個(gè)方法都調(diào)用了 MessageQueue 中都 quit(boolean safe) 方法,quitSafely 的參數(shù)為 true,quit 的參數(shù)為 false。
void quit(boolean safe) { // 主線程不退出消息循環(huán) if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { // 如果已經(jīng)退出了,直接 return if (mQuitting) { return; } // 標(biāo)記為已經(jīng)退出 mQuitting = true; // 如果 safe 的值為 true,執(zhí)行完當(dāng)前的消息后退出消息循環(huán) if (safe) { removeAllFutureMessagesLocked(); } else { // 直接退出消息循環(huán) removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
quitSafely() 會(huì)等待當(dāng)前消息執(zhí)行完畢后退出消息循環(huán),而 quit() 方法會(huì)直接退出消息循環(huán)。
private void removeAllMessagesLocked() { // 獲取當(dāng)前 MessageQueue 的頭部消息 Message p = mMessages; while (p != null) { // 循環(huán)遍歷所有的 Message Message n = p.next; // 回收消息,并把消息放入消息池 p.recycleUnchecked(); p = n; } // 將頭部消息置為空 mMessages = null; } private void removeAllFutureMessagesLocked() { // 獲取系統(tǒng)從開(kāi)機(jī)到現(xiàn)在到時(shí)間 final long now = SystemClock.uptimeMillis(); // 將當(dāng)前的頭部消息賦值給 p Message p = mMessages; if (p != null) { if (p.when > now) { // 如果當(dāng)前頭部消息將要執(zhí)行的時(shí)間大于系統(tǒng)開(kāi)機(jī)到現(xiàn)在的時(shí)間,則執(zhí)行 removeAllMessagesLocked() 方法 // 清空 MessageQueue 隊(duì)列 removeAllMessagesLocked(); } else { Message n; // 遍歷當(dāng)前的 MessageQueue,直到某個(gè)消息的執(zhí)行時(shí)間小于 now 值(即這個(gè)消息正在執(zhí)行) // 將這個(gè)消息的 next 賦值為 null for (;;) { n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } p.next = null; // 回收不會(huì)被執(zhí)行的 Message do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } } }
終于講完了,希望大家能通過(guò)我的文章,徹底理解 Handler 的機(jī)制,但我的能力有限,如果存在錯(cuò)誤的地方,還請(qǐng)指出。
零碎的東西很多,為了方便大家記憶,我把上面的內(nèi)容做成了思維導(dǎo)圖,需要的朋友可以保存下來(lái),偶爾看一下,幫助自己記憶。
歡迎關(guān)注本文作者:
掃碼關(guān)注并回復(fù)「干貨」,獲取我整理的千G Android、iOS、JavaWeb、大數(shù)據(jù)、人工智能等學(xué)習(xí)資源。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74312.html
摘要:閱讀本期周刊,你將快速入門(mén),開(kāi)啟甜蜜之旅。然則的原理負(fù)責(zé)發(fā)送以及處理消息,創(chuàng)建消息隊(duì)列并不斷從隊(duì)列中取出消息交給,則用于保存消息。 showImg(/img/bVCN99?w=900&h=385); 2016 年 8 月,Android 7.0 Nougat(牛軋?zhí)牵┱桨l(fā)布,那么問(wèn)題來(lái)了,你 Marshmallow 了么(? -? ?) Cupcake、Donut、Gingerbre...
摘要:讓你收獲滿滿碼個(gè)蛋從年月日推送第篇文章一年過(guò)去了已累積推文近篇文章,本文為年度精選,共計(jì)篇,按照類(lèi)別整理便于讀者主題閱讀。本篇文章是今年的最后一篇技術(shù)文章,為了讓大家在家也能好好學(xué)習(xí),特此花了幾個(gè)小時(shí)整理了這些文章。 showImg(https://segmentfault.com/img/remote/1460000013241596); 讓你收獲滿滿! 碼個(gè)蛋從2017年02月20...
閱讀 1830·2023-04-26 01:55
閱讀 1090·2021-09-30 09:47
閱讀 1685·2019-08-30 15:54
閱讀 750·2019-08-30 15:53
閱讀 705·2019-08-30 15:52
閱讀 1145·2019-08-30 15:44
閱讀 2421·2019-08-30 14:06
閱讀 1070·2019-08-29 16:39