<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] 一句話總結:Looper 不斷從 MessageQueue 中取出一個 Message,然后交給其對應的 Handler 處理。<!--more--> 先上一幅整體關系圖,來自郭神 ![](https://ws4.sinaimg.cn/large/006tKfTcgy1fkuqqucfpwj30j40dymy2.jpg) # Handler使用 Handler在Android中主要用于線程間的通信,經典使用方法如下: ```java Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { //... } } }; Message message = Message.obtain(); message.what = 1; mHandler.sendMessage(message); ``` 下面我們通過源碼來一起看看Handler的真面貌。 # Handler源碼分析 Handler類在整個機制中所做的事情只有兩件,一個是發布新消息,一個是處理消息,我們分別來看看。 ## Handler發布消息 Handler的sendMessage()方法源碼如下: ```java public final boolean sendMessage(Message msg){ // 默認延遲時間為0ms return sendMessageDelayed(msg, 0); } 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) { return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { // 設置Message的target為Handler自己,target是Message類的一個成員變量,為Handler類型的 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } ``` 代碼并不復雜,最終調用了MessageQueue的enqueueMessage方法將方法發送出去。enqueueMessage源碼如下: ```java boolean enqueueMessage(Message msg, long when) { // 檢查Message的target不能為空,因為后面Looper取出消息后,交由這個target(Handler)處理的 if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { msg.markInUse(); msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { // 將Message置于消息鏈表的表頭位置 msg.next = p; mMessages = msg; } else { Message prev; // 死循環遍歷Message鏈表 for (;;) { prev = p; p = p.next; // 如果當前Message的延遲執行時間比所對比的Message執行時間小,就插到它前面 // 不然繼續循環,和多對比Message的next所指向的Message繼續對比 if (p == null || when < p.when) { break; } } // 將當前Message插入消息鏈表 msg.next = p; prev.next = msg; } } return true; } ``` ![](https://img.kancloud.cn/0b/4e/0b4ef3f3318a9fb973b648dbbfb67033_834x141.png) Message插入邏輯在注釋中寫的也很清楚了,注意兩點: * MessageQueue采用單鏈表結構,表頭的Message是會最先被取出執行的 * 通過死循環遍歷鏈表,對比執行延遲時間,來決定將Message插入到何處 消息已經插入到消息隊列中了,下面我們看看消息又是怎么取出來的。 ## Handler處理消息 Handler除了插入消息到消息隊列中,還承擔著處理消息的職責。先從我們重寫的handleMessage方法作為入口。 ```java public void handleMessage(Message msg) { } ``` handleMessage是Handler類的一個空方法,需要子類來實現,我們看看這個方法是在哪里被調用的。 ```java public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } ``` dispatchMessage方法是由Looper調用的,Looper在整個Handler機制中的作用是從MessageQueue中不斷取出Message,交由Handler處理,Handler調用dispatchMessage方法進行Message的處理,Looper的源碼我們稍后分析。 Handler在處理消息時分為三種情況: ```java if (msg.callback != null) { handleCallback(msg); } private static void handleCallback(Message message) { message.callback.run(); } ``` 1、先判斷Message的callback是否為空,不為空直接調用其run方法,不再往下走 2、判斷Handler的mCallback是否為空,不為空直接調用其handleMessage方法,根據返回值決定是否往下走 3、如果Message和Handler的Callback均為空,調用Handler的handleMessage方法 其中,Message的callback在其obtain時進行設置,我們比較少手動調用帶參數的obtain方法,暫不關心;Handler的Callback則是在Handler構造的時候傳入,我們可以手動傳入;Handler的handleMessage方法是我們手動重寫的,用于處理Message。因此,我們可以通過在構造Handler時傳入Callback來處理消息,或者直接重寫Handler的handleMessage方法來處理消息。 補充剛剛Handler構造Callback的源碼: ```java public Handler(Callback callback) { this(callback, false); } public Handler(Callback callback, boolean async) { //... mCallback = callback; mAsynchronous = async; } ``` 剛剛提到Looper不斷從MessageQueue中提取消息,然后給Handler進行處理,下面我們來看看Looper: ## Looper提取消息 ### Looper的創建 Handler類有一個成員變量mLooper: ```java final Looper mLooper; ``` mLooper的初始化有兩種方式,分別如下: ```java /** * @param looper: The looper, must not be null */ public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } ``` 可以看到,mLooper一定不能為null。來看看Looper的myLooper()方法,看看如何從當前線程獲取Looper: ```java static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static @Nullable Looper myLooper() { return sThreadLocal.get(); } ``` 此處引出了ThreadLocal的概念,稍后會詳細分析。暫時可以理解為,Looper對象是當前線程的一個局部變量,其他線程無法訪問及修改, 上面是獲取Looper的方法,下面看看為當前線程設置Looper的方法,操作是在 Looper的prepare()方法完成的,看源碼: ```java private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } ``` 綜上可以看到,Looper對象是和線程綁定的,一個線程有且只能有一個Looper對象。使用Handler機制來做線程間通信時,要保證該線程的Looper不為空,也就是當我們為一個子線程開啟Handler機制時,需要先調用Looper.prepare()方法來創建Looper對象,后面Looper才可以從消息隊列中取出Message。 下面來看看Looper的構造方法: ```java private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } ``` 可以看到,Looper有兩個成員變量,一個是消息隊列,一個是當前線程。Looper是如何從消息隊列取消息的呢, 來看看: ```java public static void loop() { // 從ThreadLocal中取出Looper final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; for (;;) { Message msg = queue.next(); if (msg == null) { // 沒有消息表明消息隊列正在退出 return; } //... try { msg.target.dispatchMessage(msg); } //... } } // 前面我們在Looper.prepare()方法中,在ThreadLocal中存入新創建的Looper對象。現在取出使用 public static @Nullable Looper myLooper() { return sThreadLocal.get(); } ``` 邏輯很清晰,先從ThreadLocal中取出我們創建的Looper對象,從Looper中拿到消息隊列。然后一個for死循環,從消息隊列中循環取出消息,調用msg.target的dispatchMessage方法,msg.target就是Handler對象,也就是將消息交由Handler處理。 ### 拓展:ThreadLocal 1、ThreadLocal是一個創建線程局部變量的類。 2、一般情況下,我們創建的變量可以被任何一個線程訪問并修改,而使用ThreadLocal創建的變量只能被當前線程訪問,其他線程無法訪問和修改。 ThreadLocal使用方法: ```java // 創建 ThreadLocal<String> mStringThreadLocal = new ThreadLocal<>(); // set方法 mStringThreadLocal.set("Canton"); // get方法 mStringThreadLocal.get(); // 設置默認初始值 ThreadLocal<String> mNewStringThreadLocal = new ThreadLocal<String>() { @Override protected String initialValue() { return "A new threadLocal"; } } ``` ThreadLocal原理: 先從ThreadLocal的set方法看看ThreadLocal是如何存儲的: ```java public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) { // Thread類的threadLocals聲明如下: // ThreadLocal.ThreadLocalMap threadLocals = null; // 是一個ThreadLocalMap return t.threadLocals; } // 一個自定義的HashMap,用來存放線程局部變量值 static class ThreadLocalMap { //... } // 創建存放線程局部變量值的HashMap void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } ``` ThreadLocal的get方法源碼如下: ```java public T get() { // 獲取當前線程 Thread t = Thread.currentThread(); // 獲取當前線程的ThreadLocalMap ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); T result = (T)e.value; return result; } return setInitialValue(); } ``` 可以看到 a、ThreadLocal的set方法存入的值,實際是放到當前線程的ThreadLocalMap中,key為當前ThreadLocal對象,value為存入的對象。 b、get方法直接從當前線程的ThreadLocalMap中,根據key獲取對象。 c、注意,ThreadLocal的值并不是存放在ThreadLocal中的! ## 發送消息的其他方法 除了使用Handler的sendMessage方法,還有其他幾種方法可以發送Message到消息隊列中,總結如下: 1、使用Handler的post方法發送消息: ```java 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的sendMessage方法一樣,還是調用了Handler的sendMessageDelayed方法。區別在于,調用了getPostMessage方法為Message設置了Callback。前面處理消息時,我們分析過,如果Message有設置Callback時,直接回調Message的Callback的run方法,不再執行Handler的Callback或者Handler的handleMessage方法,這點需要注意。 2、使用View的post方法發送消息: ```java public boolean post(Runnable action) { // 當一個View添加到Window時,會在AttatchInfo中存儲一些信息,其中就有UI線程的Handler final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // 調用Handler的post方法 return attachInfo.mHandler.post(action); } // 如果View還沒被添加到一個Window中,就先將Runnable存到一個隊列中,等View添加到Window并且有Handler了再進行執行 getRunQueue().post(action); return true; } ``` 3、使用Activity的runOnUiThread方法發送消息 ```java public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { // 當前線程不是UI線程,調用UI線程的Handler將Runnable包裝成Message發送出去 mHandler.post(action); } else { // 當前線程就是UI線程,直接執行即可 action.run(); } } ``` ## 創建Handler的幾種重載 ```java // 調用兩個參數的構造方法 public Handler() { this(null, false); } // 調用兩個參數的構造方法 public Handler(Callback callback) { this(callback, false); } // 調用兩個參數的構造方法 public Handler(boolean async) { this(null, async); } // 調用三個參數的構造方法 public Handler(Looper looper) { this(looper, null, false); } // 調用三個參數的構造方法 public Handler(Looper looper, Callback callback) { this(looper, callback, false); } ``` 兩個參數和三個參數的構造方法分別如下: ```java // 兩個參數的構造方法 public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } public static @Nullable Looper myLooper() { return sThreadLocal.get(); } // 三個參數的構造方法 public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; } ``` 可以看到,Handler的構造方法重載雖然多,但最終要么調用了兩個參數的構造方法,要么調用了三個參數的構造方法。其中,三個參數的構造方法直接設置了Handler的Looper、Callback、消息是同步還是異步;兩個參數的少了一個Looper參數,直接調用了Looper.myLooper方法,來從ThreadLocal中獲取當前線程的Looper。 因此,在UI線程調用兩個參數的構造方法時,會自動獲取UI線程的Looper,并設置給Handler;但是直接在子線程調用兩個參數的構造方法時,由于還沒調用Looper.prepare(),因此子線程的Looper為空,所以會直接拋出異常。 注意! Android 中除了 UI 線程(主線程),創建的工作線程默認是沒有消息循環和消息隊列的。如果想讓該線程具有消息隊列和消息循環,并具有消息處理機制,就需要在線程中首先調用 Looper.prepare()來創建消息隊列,然后調用 Looper.loop()開啟消息循環。 ## HandlerThread 關于 HandlerThread 的使用,源碼中是這樣說的,使用 HandlerThread 可以很方便的開啟一個包含 Looper 的線程,開啟線程后,可以使用和該線程綁定的 Looper 去構建相應的 Handler。并且可以通過 HandlerThread 的 quit 和 quitSafely 方法很方便的終止線程的消息循環隊列。 HandlerThread 使用示例: ```java HandlerThread handlerThread = new HandlerThread("test"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //... } }; handlerThread.quit(); handlerThread.quitSafely(); ``` 其中HandlerThread的源碼大概如下: ```java public class HandlerThread extends Thread { @Override public void run() { Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); } onLooperPrepared(); Looper.loop(); //... } } public Looper getLooper() { //... return mLooper; } ``` 可以看到,HandlerThread繼承自Thread,在run方法中,調用Looper的prepare方法創建Looper對象并和HandlerThread進行綁定,最后調用Looper的loop方法開啟循環提取消息。我們在創建Handler時,直接將和HandleThread綁定在一起的Looper傳遞給Handler即可。這樣子Handler就會分發以及處理HandlerThread上的消息。 # 整體流程總結 1、Handler調用sendMessage、post方法發送Message,并插入到MessageQueue中,MessaQueue采用單鏈表結構。 2、Handler類有一個Looper成員變量,Looper屬于線程局部變量,每個線程有且只能有一個Looper;Looper類有一個MessageQueue成員變量;Handler持有Looper主要是為了拿到Looper對應的MessageQueue,并往其中插入消息。 3、子線程需要先調用Looper.prepare方法,來創建一個Looper對象存儲到ThreadLocal中。然后創建Handler時會調用Looper.myLooper方法獲取當前線程的Looper。 4、Looper.loop方法開啟消息循環,Looper會循環從其MessageQueue中提取消息,并調用消息的target(也就是Handler)進行分發處理 5、Handler拿到Message后,先判斷Message的Callback是否為空,不為空直接執行,消息處理結束;為空則判斷Handler的Callback是否為空,不為空則執行,并決定是否進行攔截,攔截則消息處理結束;不攔截則執行Handler的handleMessage方法。 參考文檔: [深入理解 Android 內核設計思想](https://book.douban.com/subject/25921329/) [Android消息機制(一):概述設計架構](http://www.jianshu.com/p/8656bebc27cb) [Android消息隊列模型](http://superonion.iteye.com/blog/1442416) [Android 多線程編程的總結](https://www.diycode.cc/topics/213)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看