PS:因爲感冒緣由,本篇寫的有點沒有主幹,你們湊合看吧。。異步
學習內容:ide
1.MessageQueue,Looper,MessageQueue的做用.oop
2.子線程向主線程中發送消息post
3.主線程向子線程中發送消息學習
異步消息處理機制是Android提供給咱們異步更新UI的一種很好的方式,線程之間以Handler做爲橋樑,使得Message能夠在線程間進行傳遞,從而實現異步的一種方式。ui
1.MessageQueuethis
MessageQueue顧名思義,指的就是消息隊列,說這個以前咱們首先須要知道什麼是Message,好比說咱們在UI界面時點擊一個按鈕,或者是接收到了一條廣播,其實都算是一條Message,這些事件都被封裝成一條Message被添加到了MessageQueue隊列當中,由於咱們知道一個線程在一段時間只能對一種操做進行相關的處理,所以這些消息的處理就要有前後順序,所以採用MessageQueue來管理,也就是消息隊列。消息隊列其實就是一堆須要處理的Message而造成的傳送帶。一旦有消息發送進來,那麼直接執行enqueueMessage()方法。也就是將消息壓入到隊列當中,一旦線程空閒下來,那麼直接從MessageQueue中取出消息,使得消息出隊。spa
2.Looper線程
Looper的主要做用是與當前線程造成一種綁定的關係,同時建立一個MessageQueue,這樣保證一個線程只能持有一個Looper和MessageQueue同時Looper使得MessageQueue循環起來,就比如水車和水同樣,MessageQueue比如水車,當他有了Looper的時候,那麼水車會隨着水去轉動也就是說Looper爲MessageQueue提供了活力,使其循環起來,循環的動力每每就少不了Thread。通常而言子線程通常是沒有MessageQueue,所以爲了使線程和消息隊列可以關聯那麼就須要有Looper來完成了,所以咱們能夠這樣去實現3d
class ChildRunnable implements Runnable{ @Override public void run() { Looper.prepare(); handler = new Handler(){ @Override public void handleMessage(Message msg) { Log.e("TAG",msg.obj+""); } }; Log.e("TAG_2",Looper.myLooper().toString()); Looper.loop(); } }
這裏調用了Looper的prepare()和loop()方法。
public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(true)); }
方法比較的簡單,咱們能夠看到prepare()方法首先判斷了sThreadLocal中持有的線程引用是否爲空,若是不爲空,那麼直接就會拋異常,這也就說明了Looper.prepare()在一個線程中只容許調用一次,這樣也一樣爲一個線程對應一個Looper作了保障。當Looper.prepare()執行完畢以後Looper才能夠執行loop()方法。
public static void loop() { /** * 獲取當前線程綁定的Looper */ final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } /** * 當前線程的MessageQueue */ final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); /** * 死循環,循環從MessageQueue取出消息. */ for (;;) { /** * 從Queue中取出一條消息 */ Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } /** * 將消息分發出去 */ msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. 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.recycle(); } }
這裏咱們能夠看到loop()是經過幾個流程使得MessageQueue循環起來的,首先經過靜態方法獲取當前線程對應的Looper,而後獲取由Looper建立的MessageQueue,而後進入死循環,阻塞式的從MessageQueue中取出消息,獲取到消息以後將消息分發出去,不然進入等待狀態,等待新的消息到來。這就是Looper使MessageQueue循環起來的方式。
那麼咱們如今MessageQueue消息隊列已經存在了,Looper以線程爲動力使得MessageQueue循環了起來,那麼消息的發送和處理由誰來完成呢顯而易見這就是Handler的做用了.
3.Handler
Handler的出現使得消息能夠被髮送和接收,Handler的構造方法有多個。
public Handler() { } public Handler(Handler.Callback callback) { } public Handler(Looper looper) { } public Handler(Looper looper, Handler.Callback callback) { }
這是Handler的四個構造方法,1和2都沒有傳遞Looper,那麼Handler經過調用Looper.myLooper()方法將Handler與Looper和MessageQueue造成綁定關係,而3和4直接傳遞Looper,那麼Handler將直接將傳遞進來的Looper對象進行保存,直接和傳遞的Looper以及相關的MessageQueue造成綁定關係。同時這四個構造方法還有不一樣點,就是是否傳遞Callback回調接口對應的參數。
public interface Callback { public boolean handleMessage(Message msg); }
實現Callback接口是實現處理Message的一種方式,而另外一種方式則不傳遞Callback回調接口,而是直接實現Handler中handleMessage方法,也就是說咱們能夠經過這兩種方式實現Handler對消息的處理。這樣三者就造成了綁定關係,而後咱們來看看Handler的sendMessage等方法.其實不管是調用了哪一種方法,sendMessage(),sendMessageDelayed()等等,最後都會調用sendMessageAtTime()方法。這裏須要先說一下post方法和sendMessage方法。通常咱們使用Handler發送消息的時候也會這樣去寫。
handler.post(new Runnable() { @Override public void run() { /** * 相關操做 * */ } });
這樣發送消息也是能夠的,post執行過程是這樣的。
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; }
post方法也是發送了一條消息,runnable則做爲callback參數做爲回調,用於後續處理消息。這裏也調用了sendMessageDelayed()方法,最後仍是會調用sendMessageAtTime()方法,所以能夠看出,不管是send仍是post,最後都會調用sendMessageAtTime()方法。
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; return enqueueMessage(queue, msg, uptimeMillis); }
sendMessageAtTime()是最終調用的方法,他經過調用enqueueMessage將消息加入到消息隊列當中。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //注意下面這行代碼 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //注意下面這行代碼 return queue.enqueueMessage(msg, uptimeMillis); }
enqueueMessage方法比較的簡單,這裏講msg.target = this,將msg的target賦值爲當前的handler,而後調用queue.enqueueMessage()方法將消息加入到消息隊列當中。那麼SendMessage以後,Looper就經過loop()方法不斷的從MessageQueue取出消息,而後經過dispatchMessage()方法將消息分發給Handler,交給Handler去處理相關的Message.
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
這樣咱們就能夠直接去處理相關的消息了,這個方法作了一些判斷,也就是判斷是否傳遞了callback屬性,若是msg.callback不爲空,那麼則表示是post發送過來的消息,在處理的時候就須要調用handleCallback(msg)方法,而後咱們會繼續判斷Handler的Callback是否爲空,也就是咱們在new Handler(Handler.Callback)方法去建立Handler的時候的判斷,若是不爲空,那麼執行咱們Callback中實現的handleMessage()方法,若是爲空,那麼就直接執行Handler中的handleMessage()方法。整體的流程就是這樣。在其餘的牛人博客中,看到了一幅特別形象的流程圖。在這裏貼出來。
在現實生活的生產生活中,存在着各類各樣的傳送帶,傳送帶上面灑滿了各類貨物,傳送帶在發動機滾輪的帶動下一直在向前滾動,不斷有新的貨物放置在傳送帶的一端,貨物在傳送帶的帶動下送到另外一端進行收集處理。
咱們能夠把傳送帶上的貨物看作是一個個的Message,而承載這些貨物的傳送帶就是裝載Message的消息隊列MessageQueue。傳送帶是靠發送機滾輪帶動起來轉動的,咱們能夠把發送機滾輪看作是Looper,而發動機的轉動是須要電源的,咱們能夠把電源看作是線程Thread,全部的消息循環的一切操做都是基於某個線程的。一切準備就緒,咱們只須要按下電源開關發動機就會轉動起來,這個開關就是Looper的loop方法,當咱們按下開關的時候,咱們就至關於執行了Looper的loop方法,此時Looper就會驅動着消息隊列循環起來。
那Hanlder在傳送帶模型中至關於什麼呢?咱們能夠將Handler看作是放入貨物以及取走貨物的管道:貨物從一端順着管道劃入傳送帶,貨物又從另外一端順着管道劃出傳送帶。咱們在傳送帶的一端放入貨物的操做就至關於咱們調用了Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法,這就把Message對象放入到了消息隊列MessageQueue中了。當貨物從傳送帶的另外一端順着管道劃出時,咱們就至關於調用了Hanlder的dispatchMessage方法,在該方法中咱們完成對Message的處理。
2.子線程向主線程發送消息
子線程向主線程發送消息,平時仍是很經常使用的,好比說子線程完成了一個耗時的操做,須要主線程去更新UI,那麼這個時候就須要子線程向主線程中發送相關的消息。用起來也比較的簡單。
private Handler handler = new Handler(){ //主線程作處理. @Override public void handleMessage(Message msg) { String data = (String) msg.obj; textView.setText("從子線程發送過來的消息"+data); childRunnable = null; } }; class ChildRunnable implements Runnable { //子線程發送相關消息 @Override public void run() { Message message = Message.obtain(); message.obj = "ChildToMain"; handler.sendMessage(message); } }
3.主線程將消息發送給子線程
咱們常常將消息從子線程發送給主線程,其實主線程也是能夠發消息給子線程的。在默認狀況下子線程是沒有Looper和MessageQueue的,所以咱們須要爲子線程建立一個Looper而後與子線程的Handler造成綁定關係。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_main_to_child); Log.e("TAG_0",Looper.getMainLooper().toString()); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Message message = Message.obtain(); message.obj = "MainToChild"; handler.sendMessage(message); } }); new Thread(new ChildRunnable()).start(); } class ChildRunnable implements Runnable{ @Override public void run() { Looper.prepare(); handler = new Handler(){ @Override public void handleMessage(Message msg) { Log.e("TAG",msg.obj+""); } }; Log.e("TAG_2",Looper.myLooper().toString()); Looper.loop(); } }
這裏設置了一個按鈕的監聽事件來控制主線程向子線程發送消息,或者咱們先讓主線程休眠,先讓子線程初始化完畢以後,主線程再向子線程發送消息,若是直接發送的話,頗有可能在Handler尚未初始化完畢後就致使消息發送,這樣就會出現Handler爲null從而致使NullPointerExpection發生。所以在這裏進行了簡單的控制,這裏咱們能夠看到,若是子線程想擁有本身的Looper和MessageQueue首先須要執行Looper.prepare()和Looper.loop()方法。才能爲子線程的Handler綁定上Looper和MessageQueue。有人可能會問道,爲何主線程直接發送消息就能夠,而不須要調用這兩個方法,這是由於Activity在onCreate的時候已經執行了這些方法,所以咱們能夠直接發消息給主線程。
所以當咱們在使用Handler Message Looper實現異步消息機制的時候,若是想實現多級通訊,那麼就須要弄明白當前的Looper MessageQueue綁定的是哪一個Handler。Looper,MessageQueue在一個線程中只有惟一一個,可是Handler能夠是多個的,咱們只須要控制這個綁定關係是實現多級通訊的關鍵。
最後貼一個簡單的源代碼:Demo下載