PS:由於感冒原因,本篇寫的有點沒有主干,大家湊合看吧。。
學習內容:
1.MessageQueue,Looper,MessageQueue的作用.
2.子線程向主線程中發送消息
3.主線程向子線程中發送消息
異步消息處理機制是Android提供給我們異步更新UI的一種很好的方式,線程之間以Handler作為橋梁,使得Message可以在線程間進行傳遞,從而實現異步的一種方式。
1.MessageQueue
MessageQueue顧名思義,指的就是消息隊列,說這個之前我們首先需要知道什么是Message,比如說我們在UI界面時點擊一個按鈕,或者是接收到了一條廣播,其實都算是一條Message,這些事件都被封裝成一條Message被添加到了MessageQueue隊列當中,因為我們知道一個線程在一段時間只能對一種操作進行相關的處理,因此這些消息的處理就要有先后順序,因此采用MessageQueue來管理,也就是消息隊列。消息隊列其實就是一堆需要處理的Message而形成的傳送帶。一旦有消息發送進來,那么直接執行enqueueMessage()方法。也就是將消息壓入到隊列當中,一旦線程空閑下來,那么直接從MessageQueue中取出消息,使得消息出隊。
2.Looper
Looper的主要作用是與當前線程形成一種綁定的關系,同時創建一個MessageQueue,這樣保證一個線程只能持有一個Looper和MessageQueue同時Looper使得MessageQueue循環起來,就好比水車和水一樣,MessageQueue好比水車,當他有了Looper的時候,那么水車會隨着水去轉動也就是說Looper為MessageQueue提供了活力,使其循環起來,循環的動力往往就少不了Thread。一般而言子線程一般是沒有MessageQueue,因此為了使線程和消息隊列能夠關聯那么就需要有Looper來完成了,因此我們可以這樣去實現
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下載