您好,登錄后才能下訂單哦!
這篇文章主要介紹了Android中Handler,MessageQueue與Looper關系是什么,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
一說到Android的消息機制,自然就會聯想到Handler,我們知道Handler是Android消息機制的上層接口,因此我們在開發過程中也只需要和Handler交互即可,很多人認為Handler的作用就是更新UI,這也確實沒錯,但除了更新UI,Handler其實還有很多其他用途,比如我們需要在子線程進行耗時的I/O操作,可能是讀取某些文件或者去訪問網絡等,當耗時操作完成后我們可能需要在UI上做出相應的改變,但由于Android系統的限制,我們是不能在子線程更新UI控件的,否則就會報異常,這個時候Handler就可以派上用場了,我們可以通過Handler切換到主線程中執行UI更新操作。
下面是Handler一些常用方法:
void handleMessage(Message msg):處理消息的方法,該方法通常會被重寫。
final boolean hasMessages(int what):檢測消息隊列中是否包含what屬性為指定值的消息。
Message obtainMessage():獲取消息的方法,此函數有多個重載方法。
sendEmptyMessage(int what):發送空消息。
final boolean sendEmptyMessageDelayed(int what , long delayMillis):指定多少毫秒后發送空消息。
final boolean sendMessage(Message msg):立即發送消息。
final boolean sendMessageDelayed(Message msg ,long delayMillis):指定多少毫秒后發送消息。
final boolean post(Runnable r):執行runnable操作。
final boolean postAtTime(Runnable r, long upTimeMillis):在指定時間執行runnable操作。
final boolean postDelayed(Runnable r, long delayMillis):指定多少毫秒后執行runnable操作。
介紹完方法后,我們就從一個簡單的例子入手吧,然后一步步的分析:
public class MainActivity extends AppCompatActivity { public static final int MSG_FINISH = 0X001; //創建一個Handler的匿名內部類 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_FINISH: LogUtils.e("handler所在的線程id是-->" + Thread.currentThread().getName()); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //啟動耗時操作 consumeTimeThread(findViewById(R.id.tv)); // handler.post() } //啟動一個耗時線程 public void consumeTimeThread(View view) { new Thread() { public void run() { try { LogUtils.e("耗時子線程的Name是--->" + Thread.currentThread().getName()); //在子線程運行 Thread.sleep(2000); //完成后,發送下載完成消息 handler.sendEmptyMessage(MSG_FINISH); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } }
運行結果:
上面的例子其實就是Handler的基本使用,在主線中創建了一個Handler對象,然后通過在子線程中模擬一個耗時操作完成后通過sendEmptyMessage(int)方法發送一個消息通知主線程的Handler去執行相應的操作。通過運行結果我們也可以知道Handler確實也是在主線程運行的。
那么問題來了,通過Handler發送的消息是怎么到達主線程的呢?接下來我們就來掰掰其中的奧妙,前方高能,請集中注意力!為了更好的理解Handler的工作原理,我們先來介紹與Handler一起工作的幾個組件:
Message:Handler接收和處理消息的對象。
Looper:每個線程只能有一個Looper。它的loop方法負責讀取MessageQueue中的消息,讀到消息后把消息發送給Handler進行處理。
MessageQueue:消息隊列,它采用先進先出的方式來管理Message。程序創建Looper對象時,會在它的構造方法中創建MessageQueue對象。
Handler:它的作用有兩個—發送消息和處理消息,程序使用Handler發送消息,由Handler發送的消息必須被送到指定的MessageQueue;否則消息就沒有在MessageQueue進行保存了。而MessageQueue是由Looper負責管理的,也就是說,如果希望Handler正常工作的話,就必須在當前線程中有一個Looper對象。我們先對上面的幾個組件有大概的了解就好,后面我們都會詳細分析,既然消息是從Handler發送出去,那么我們就先從Handler入手吧。先來看看Handler 的構造方法源碼:
public class Handler { /** * 未實現的空方法handleMessage() */ public void handleMessage(Message msg) { } /** * 我們通常用于創建Handler的構造方法之一 */ public Handler() { this(null, false); } // 構造方法的內調用的this(null, false)的具體實現 public Handler(Callback callback, boolean async) { //檢查Handler是否是static的,如果不是的,那么有可能導致內存泄露 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()); } } //重要的組件出現啦!Looper我們先理解成一個消息隊列的管理者,用來從消息隊列中取消息的,后續會詳細分析 mLooper = Looper.myLooper(); if (mLooper == null) { //這個異常很熟悉吧,Handler是必須在有Looper的線程上執行,這個也就是為什么我在HandlerThread中初始化Handler //而沒有在Thread里面初始化,如果在Thread里面初始化需要先調用Looper.prepare方法 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //將mLooper里面的消息隊列復制到自身的mQueue,這也就意味著Handler和Looper是公用一個消息隊列 mQueue = mLooper.mQueue; //回調函數默認是Null mCallback = null; }
分析:Handler的構造方法源碼不是很多,也比較簡單,但是我們從源碼中也可以得知,在創建Handler時,Handler內部會去創建一個Looper對象,這個Looper對象是通過Looper.myLooper()創建的(后續會分析這個方法),同時還會創建一個消息隊列MessageQueue,而這個MessageQueue是從Looper中獲取的,這也就意味著Handler和Looper共用一個消息隊列,當然此時Handler,Looper以及MessageQueue已經捆綁到一起了。上面還有一個情況要說明的,那就是:
if (mLooper == null) { //這個異常很熟悉吧,Handler是必須在有Looper的線程上執行,這個也就是為什么我在HandlerThread中初始化Handler //而沒有在Thread里面初始化,如果在Thread里面初始化需要先調用Looper.prepare方法 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); }
這里先回去判斷Looper是否為空,如果為null,那么就會報錯,這個錯誤對我們來說應該比較熟悉吧,那為什么會報這個錯誤呢?我們在前面說過Handler的作用有兩個—發送消息和處理消息,我們在使用Handler發送消息,由Handler發送的消息必須被送到指定的MessageQueue;否則就無法進行消息循環。而MessageQueue是由Looper負責管理的,也就是說,如果希望Handler正常工作的話,就必須在當前線程中有一個Looper對象。那么又該如何保障當前線程中一定有Looper對象呢?這里其實分兩種情況:
(1)在主UI線程中,系統已經初始化好了一個Looper對象,因此我們可以直接創建Handler并使用即可。
(2)在子線程中,我們就必須自己手動去創建一個Looper對象,并且去啟動它,才可以使用Handler進行消息發送與處理。使用事例如下:
class childThread extends Thread{ public Handler mHandler; @Override public void run() { //子線程中必須先創建Looper Looper.prepare(); mHandler =new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //處理消息 } }; //啟動looper循環 Looper.loop(); } }
分析完Handler的構造方法,我們接著看看通過Handler發送的消息到底是發送到哪里了?我們先來看看Handler的幾個主要方法源碼:
// 發送一個空消息的方法,實際上添加到MessagerQueue隊列中 public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } // 給上一個方法調用 public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } // 給上一個方法調用 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;// 設置發送目標對象是Handler本身 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中 } // 在looper類中的loop()方法內部調用的方法 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
分析:通過源碼我們可以知道,當我們調用sendEmptyMessage(int)發送消息后。最終Handler內部會去調用enqueueMessage(MessageQueue queue,Message msg)方法把發送的消息添加到消息隊列MessageQueue中,同時還有設置msg.target=this此時就把當前handler對象綁定到msg.target中了,這樣就完成了Handler向消息隊列存放消息的過程。這個還有一個要注意的方法 dispatchMessage(Message),這個方法最終會在looper中被調用(這里我們先知道這點就行,后續還會分析)。話說我們一直在說MessageQueue消息隊列,但這個消息隊列到底是什么啊?其實在Android中的消息隊列指的也是MessageQueue,MessageQueue主要包含了兩種操作,插入和讀取,而讀取操作本身也會伴隨著刪除操作,插入和讀取對應的分別是enqueueMessage和next,其中enqueueMessage是向消息隊列中插入一條消息,而next的作用則是從消息隊列中取出一條消息并將其從隊列中刪除。雖然我們一直稱其為消息隊列但是它的內部實現并不是隊列,而是通過一個單鏈表的數據結構來維護消息列表的,因為我們知道單鏈表在插入和刪除上比較有優勢。至內MessageQueue的內部實現,這個屬于數據結構的范疇,我們就不過多討論了,還是回到原來的主題上來,到這里我們都知道Handler發送的消息最終會添加到MessageQueue中,但到達MessageQueue后消息又是如何處理的呢?還記得我們前面說過MessageQueue是由Looper負責管理的吧,現在我們就來看看Looper到底是如何管理MessageQueue的?
public final class Looper { // sThreadLocal.get() will return null unless you've called prepare(). //存放線程的容器類,為確保獲取的線程和原來的一樣 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class //消息隊列 final MessageQueue mQueue; final Thread mThread; //perpare()方法,用來初始化一個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"); } sThreadLocal.set(new Looper(quitAllowed)); } //handler調用的獲取Looper對象的方法。實際是在ThreadLocal中獲取。 public static Looper myLooper() { return sThreadLocal.get(); } //Looper類的構造方法,可以發現創建Looper的同時也創建了消息隊列MessageQueue對象 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); } //這個方法是給系統調用的,UI線程通過調用這個線程,從而保證UI線程里有一個Looper //需要注意:如果一個線程是UI線程,那么myLooper和getMainLooper是同一個Looper public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; } } //獲得UI線程的Looper,通常我們想Hanlder的handleMessage在UI線程執行時通常會new Handler(getMainLooper()); public synchronized static final Looper getMainLooper() { return mMainLooper; } //looper中最重要的方法loop(),該方法是個死循環,會不斷去消息隊列MessageQueue中獲取消息,然后調dispatchMessage(msg)方法去執行 public static void loop() { 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(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //這里其實就是調用handler中的方法,而在Handler的源碼中也可以知道dispatchMessage(msg)內部調用的就是handlerMessage()方法 msg.target.dispatchMessage(msg); msg.recycle(); } }
分析:代碼不算多,我們拆分開慢慢說,在Looper源碼中我們可以得知其內部是通過一個ThreadLocal的容器來存放Looper的對象本身的,這樣就可以確保每個線程獲取到的looper都是唯一的。那么Looper對象是如何被創建的呢?通過源碼我們可以知道perpare()方法就可以創建Looper對象:
//perpare()方法,用來初始化一個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"); } sThreadLocal.set(new Looper(quitAllowed)); }
在創建Looper對象前先會去判斷ThreadLocal中是否已經存在Looper對象,如果不存在就新創建一個Looper對象并且存放ThreadLocal中。這里還有一個要注意的是在Looper創建的同時MessageQueue消息隊列也被創建完成,這樣的話Looper中就持有了MessageQueue對象。
//Looper類的構造方法,可以發現創建Looper的同時也創建了消息隊列MessageQueue對象 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
那么我們如何獲取已經創建好的Looper對象呢?通過源碼我們知道myLooper()方法就可以獲取到Looper對象:
//handler調用的獲取Looper對象的方法。實際是在ThreadLocal中獲取。 public static Looper myLooper() { return sThreadLocal.get(); }
Looper對象的創建和獲取,還有MessageQueue對象的創建,現在我們都很清楚了,但是Looper到底是怎么管理MessageQueue對象的呢?這就要看looper()方法了:
//looper中最重要的方法loop(),該方法是個死循環, //會不斷去消息隊列MessageQueue中獲取消息, //然后調dispatchMessage(msg)方法去執行 public static void loop() { 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(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //這里其實就是調用handler中的方法,而在Handler的源碼中也可以知道dispatchMessage(msg)內部調用的就是handlerMessage()方法 msg.target.dispatchMessage(msg); msg.recycle(); }
通過looper()方法內部源碼我們可以知道,首先會通過myLoooper()去獲取一個Looper對象,如果Looper對象為null,就會報出一個我們非常熟悉的錯誤提示,“No Looper;Looper.prepare() wasn't called on this thread”,要求我們先通過Looper.prepare()方法去創建Looper對象;如果Looper不為null,那么就會去獲取消息隊列MessageQueue對象,接著就進入一個for的死循環,不斷從消息隊列MessageQueue對象中獲取消息,如果消息不為空,那么久會調用msg.target的dispatchMessage(Message)方法,那么這個target又是什么,沒錯target就是我們創建的Handler對象,還記得我們前面分析Handler源碼時說過的那個方法嘛?
// 在looper類中的loop()方法內部調用的方法 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }
現在明白了吧?首先,檢測Message的callback是否為null,不為null就通過handleCallback方法來處理消息,那么Message的callback是什么?其實就是一個Runnable對象,實際上就是Handler的post方法所傳遞的Runnable參數,我們順便看看post方法源碼:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
現在明白Message的callback是什么了吧?而對應handleCallback方法邏輯也比較簡單:
private static void handleCallback(Message message) { message.callback.run(); }
嗯,是的,因此最終執行的還是通過post方法傳遞進來的Runnable參數的run方法。好了,我們繼續dispatchMessage()方法的分析,接著會去檢查mCallback是否為null,不為null,則調用mCallback的handleMessage方法來處理消息。至于Callback則就是一個接口定義如下:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); }
這個接口有什么用呢?其實通過Callback接口我們就可以采取如下方法來創建Handler對象:
Handler handler =new Handler(callback)
那么這樣做到底有什么意義,其實這樣做可以用callback來創建一個Handler的實例而無需派生Handler的子類。在我們的開發過程中,我們經常使用的方法就是派生一個Hanlder子類并重寫其handleMessage方法來處理具體的消息,而Callback給我們提供了另外一種方式,那就是當我們不想派生子類的時候,可以通過Callback來實現。繼續dispatchMessage()方法的分析,最后如果以上條件都不成立的話,就會去調用Handler的handleMessage方法來處理消息。而 我們的Handler是在主線程創建的,也就是說Looper也是主線程的Looper,因此handleMessage內部處理最終都會在主線程上執行,就這樣整個流程都執行完了。下面提供一個圖解幫助大家理解:
最后我們來個小總結:Android中的Looper類主要作用是來封裝消息循環和消息隊列的,用于在android線程中進行消息處理。handler是用來向消息隊列中插入消息的并最好對消息進行處理。
(1) Looper類主要是為每個線程開啟的單獨的消息循環。 默認情況下android中新誕生的線程是沒有開啟消息循環的。(主線程除外,主線程系統會自動為其創建Looper對象,開啟消息循環) Looper對象負責管理MessageQueue,而MessageQueue主要是用來存放handler發送的消息,而且一個線程只能有一個Looper,對應一個MessageQueue。
(2) 我們通常是通過Handler對象來與Looper進行交互的。Handler可看做是Looper的一個接口,用來向指定的Looper中的MessageQueue發送消息并且Handler還必須定義自己的處理方法。 默認情況下Handler會與其被定義時所在線程的Looper綁定,如Handler在主線程中定義,它是與主線程的Looper綁定。 mainHandler = new Handler() 等價于 new Handler(Looper.myLooper())Looper.myLooper():獲取當前進程的looper對象, Looper.getMainLooper() 用于獲取主線程的Looper對象。
(3) 在非主線程中直接new Handler() 會報如下的錯誤: Can't create handler inside thread that has not called Looper.prepare() 原因是非主線程中默認沒有創建Looper對象,需要先調用Looper.prepare()啟用Looper,然后再調用Looper.loop()。
(4) Looper.loop():啟動looper中的循環線程,Handler就會從消息隊列里取消息并進行對應處理。 最后要注意的是寫在Looper.loop()之后的代碼不會被執行,這個函數內部應該是一個循環,當調用mHandler.getLooper().quit()后,loop()才會中止,其后的代碼才能得以運行。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Android中Handler,MessageQueue與Looper關系是什么”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。