Android Handler 消息循環機制


前言

一問起Android應用程序的入口,很多人會說是Activity中的onCreate方法,也有人說是ActivityThread中的靜態main方法。因為Java虛擬機在運行的時候會自動加載指定類的靜態共有main方法,因此個人更傾向於第二種說法。

  1. public final class ActivityThread
  2. ...... 
  3. public static void main(String[] args)
  4. ...... 
  5. Looper.prepareMainLooper(); 
  6. ActivityThread thread = new ActivityThread(); 
  7. thread.attach(false); 
  8. if (sMainThreadHandler == null) { 
  9. sMainThreadHandler = thread.getHandler(); 

  10. ...... 
  11. Looper.loop(); 
  12. throw new RuntimeException("Main thread loop unexpectedly exited"); 


我們注意到這段代碼,先調用Looper的prepareMainLooper()方法,新建一個ActivityThread,然后再獲取MainThreadHander,最后調用Looper.loop()方法,程序就一直在loop方法中循環。Looper,Handler之間有何關系?請看下文。

程序的消息循環

  1. class test
  2. public static void main (String[] args)
  3. System.out.println("hello world!"); 


上述小程序就是一個任務,虛擬機啟用一個線程執行完該任務后,程序就結束了。為了保證程序不立即退出,一般都會寫一個循環

  1. class test
  2. public static void main (String[] args)
  3. while (hasNextMessage()) { 
  4. msg = getMessage() ;  
  5. handleMessage(msg) ; 



系統不斷從getMessage獲取消息,再通過handleMessage來處理消息。這種基於消息的循環模型在許多的系統框架中都有實現。比如iOS系統中的RunLoop,再比如windows系統中的消息隊列,windows系統會為每一個UI線程分配一個消息隊列,發生輸入事件后,windows將事件轉換為一個"消息"發送給系統消息隊列,操作系統有一個專門的線程從系統消息隊列中取出消息,分發給各個UI線程的輸入消息隊列中。Android中的應用系統框架也不例外,也有一套自己的消息循環機制,這套機制是靠Looper、Handler、MessageQueue來共同完成的。

原理

p1.jpg

p1.jpg

圖片來自 zongpeiqing的CSDN博客。Looper負責消息循環(例子中的while語句),Handler負責發送和處理消息,MessageQueue則負責管理消息(消息的增加和移除)。不管是sendMessage還是sendMessageDelay或是View.post方法,或是上面列出的沒列出的發送消息的方法,最終都會包裝成一條消息Message(這條消息由Handler發出),然后調用MessageQuque的enqueueMessage方法把消息放到消息隊列MessageQueue中。而Looper則會不停地查看MessageQueue中是否有新消息msg,有新消息就會調用新消息msg.target.handleMessage()去處理消息,否則會一直阻塞。msg.target實際上是Handler對象。因此,Handler發送的消息,最終也是由Handler來處理。

問題

Q1: Handler是怎么在不同線程之間切換的?

  1. public final class Looper
  2. ...... 
  3. public static void loop()
  4. final Looper me = myLooper(); 
  5. if (me == null) { 
  6. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 

  7. final MessageQueue queue = me.mQueue; 
  8. ........ 
  9. for (;;) { 
  10. Message msg = queue.next(); // might block 
  11. if (msg == null) { 
  12. // No message indicates that the message queue is quitting. 
  13. return

  14. ...... 
  15. msg.target.dispatchMessage(msg); 
  16. ...... 
  17. msg.recycleUnchecked(); 



  1. public class Handler
  2. /* 
  3. * Set this flag to true to detect anonymous, local or member classes 
  4. * that extend this Handler class and that are not static. These kind 
  5. * of classes can potentially create leaks. 
  6. */ 
  7. private static final boolean FIND_POTENTIAL_LEAKS = false
  8. private static final String TAG = "Handler"
  9.  
  10. /** 
  11. * Callback interface you can use when instantiating a Handler to avoid 
  12. * having to implement your own subclass of Handler. 
  13. * 
  14. * @param msg A {@link android.os.Message Message} object 
  15. * @return True if no further handling is desired 
  16. */ 
  17. public interface Callback
  18. public boolean handleMessage(Message msg)

  19.  
  20. /** 
  21. * Subclasses must implement this to receive messages. 
  22. */ 
  23. public void handleMessage(Message msg)

  24.  
  25. /** 
  26. * Handle system messages here. 
  27. */ 
  28. public void dispatchMessage(Message msg)
  29. if (msg.callback != null) { 
  30. handleCallback(msg); 
  31. } else
  32. if (mCallback != null) { 
  33. if (mCallback.handleMessage(msg)) { 
  34. return


  35. handleMessage(msg); 



首先在線程1新建一個handler,在線程2 新建一條消息msg,然后在線程2調用hander.sendMessage(msg),因為在handler的處理邏輯handleMessage()方法是放在線程1的,因此在線程2中調用了hander.sendMessage(msg),MessagQueue插入了這條消息,Looper發現有新消息,然后取出新消息,調用msg.target.dispatchMessage(msg),上面已經說到,target其實是hander,這樣便成功地切換到線程1的handleMessage邏輯上來了。最常見的例子就是在Activity中聲明一個Handler,然后異步線程去請求網絡,再通過網絡更新UI。可以參考這里

Q2: 上面的模型中,為何不直接使用Handler來循環消息?

這個問題仁者見仁,智者見智,這只是一種實現消息循環的方法之一,而不是唯一方法。不是說一定要用Looper。

Q3: 需要注意的點?

使用Looper前一定要調用Looper.prepare生成線程內(TheadLocal存儲)的Looper實例,然后才能調用Looper.loop()實現消息循環。下面是一個非主線程中Looper和Handler演示例子,此時整個線程是不會自動停止的,會一直阻塞,直到調用了Looper.quit()方法才會停止。(下面代碼僅僅是為了演示,沒有做任何事)

  1. new Thread("example-1") { 
  2. Handler handler = new Handler() { 
  3. @Override 
  4. public void handleMessage(Message msg)
  5. // 自己的處理邏輯 
  6. if (條件滿足) { 
  7. Looper.quit() ; //退出消息循環,結束線程 


  8. } ; 
  9. @Override 
  10. public void run()
  11. Looper.prepare(); 
  12. // do something.... 
  13. Message msg = Message.obtain(); 
  14. msg.what = ... 
  15. msg.obj = ... 
  16. ....... 
  17. handler.sendMessage(msg); 
  18. Looper.loop(); 

  19. }.start() ; 

Q4: 為何ActivityThread中的Looper.loop()沒有阻塞主線程?

。。。。。

參考

《Android開發藝術探索》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM