前言
一問起Android應用程序的入口,很多人會說是Activity中的onCreate方法,也有人說是ActivityThread中的靜態main方法。因為Java虛擬機在運行的時候會自動加載指定類的靜態共有main方法,因此個人更傾向於第二種說法。
- public final class ActivityThread {
- ......
- public static void main(String[] args) {
- ......
- Looper.prepareMainLooper();
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- if (sMainThreadHandler == null) {
- sMainThreadHandler = thread.getHandler();
- }
- ......
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
- }
我們注意到這段代碼,先調用Looper的prepareMainLooper()方法,新建一個ActivityThread,然后再獲取MainThreadHander,最后調用Looper.loop()方法,程序就一直在loop方法中循環。Looper,Handler之間有何關系?請看下文。
程序的消息循環
- class test {
- public static void main (String[] args) {
- System.out.println("hello world!");
- }
- }
上述小程序就是一個任務,虛擬機啟用一個線程執行完該任務后,程序就結束了。為了保證程序不立即退出,一般都會寫一個循環
- class test {
- public static void main (String[] args) {
- while (hasNextMessage()) {
- msg = getMessage() ;
- handleMessage(msg) ;
- }
- }
- }
系統不斷從getMessage獲取消息,再通過handleMessage來處理消息。這種基於消息的循環模型在許多的系統框架中都有實現。比如iOS系統中的RunLoop,再比如windows系統中的消息隊列,windows系統會為每一個UI線程分配一個消息隊列,發生輸入事件后,windows將事件轉換為一個"消息"發送給系統消息隊列,操作系統有一個專門的線程從系統消息隊列中取出消息,分發給各個UI線程的輸入消息隊列中。Android中的應用系統框架也不例外,也有一套自己的消息循環機制,這套機制是靠Looper、Handler、MessageQueue來共同完成的。
原理

圖片來自 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是怎么在不同線程之間切換的?
- public final class Looper {
- ......
- 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;
- }
- ......
- msg.target.dispatchMessage(msg);
- ......
- msg.recycleUnchecked();
- }
- }
- }
- public class Handler {
- /*
- * Set this flag to true to detect anonymous, local or member classes
- * that extend this Handler class and that are not static. These kind
- * of classes can potentially create leaks.
- */
- private static final boolean FIND_POTENTIAL_LEAKS = false;
- private static final String TAG = "Handler";
-
- /**
- * 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);
- }
-
- /**
- * Subclasses must implement this to receive messages.
- */
- public void handleMessage(Message msg) {
- }
-
- /**
- * Handle system messages here.
- */
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- 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()方法才會停止。(下面代碼僅僅是為了演示,沒有做任何事)
- new Thread("example-1") {
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // 自己的處理邏輯
- if (條件滿足) {
- Looper.quit() ; //退出消息循環,結束線程
- }
- }
- } ;
- @Override
- public void run() {
- Looper.prepare();
- // do something....
- Message msg = Message.obtain();
- msg.what = ...
- msg.obj = ...
- .......
- handler.sendMessage(msg);
- Looper.loop();
- }
- }.start() ;
Q4: 為何ActivityThread中的Looper.loop()沒有阻塞主線程?
。。。。。