1. 程式人生 > >Android架構分析之Android訊息處理機制(二)

Android架構分析之Android訊息處理機制(二)

作者:劉昊昱 

Android版本:4.4.2

在上一篇文章中我們看了一個使用Handler處理Message訊息的例子,本文我們來分析一下其背後隱藏的Android訊息處理機制。

我們可能比較熟悉Windows作業系統的訊息處理模型:

while(GetMessage(&msg,NULL, 0, 0))
{
       TranslateMessage(&msg);
       DispatchMessage(&msg);
}


1、訊息被投遞到訊息佇列中。

2、應用程式在訊息處理迴圈中呼叫GetMessage函式從訊息佇列中取出訊息(直到取得WM_QUIT訊息,才會退出訊息處理迴圈)。

3、呼叫TranslateMessage函式對訊息進行必要的處理,例如放棄對某些訊息的響應。

4、呼叫DispatchMessage函式將訊息分配給對應物件處理。

Android作為圖形化的作業系統,其訊息處理機制與Windows是類似的,同樣有一個訊息處理迴圈從訊息佇列中取出訊息,然後將訊息傳送給相應的物件處理。

在Android系統中,訊息處理迴圈對應的類是Looper,訊息對應的類是Message,訊息佇列對應的類是MessageQueue,訊息的處理和分發是通過Handler類完成的。

首先我們來分析Looper,該類定義在frameworks/base/core/java/android/os/Looper.java檔案中。

Android文件對Looper類的說明如下:

Class used to run amessage loop for a thread. Threads by default do not have a message loopassociated with them; to create one, call inthe thread that is to run the loop, and then tohave it process messages until the loop is stopped.

Most interaction with amessage loop is through the 

class.

This is a typicalexample of the implementation of a Looper thread, using the separation of and tocreate an initial Handler to communicate with the Looper.

  class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

從上面Android文件對Looper類的說明中可以看到,對於一個執行緒,要建立訊息處理迴圈,需要呼叫Looper.prepare()函式和Looper.loop()函式。其中,Looper.prepare()完成必要的初始化工作,Looper.loop()完成迴圈取訊息,分發訊息的工作。

先來看Looper.prepare()函式。

71     /** Initialize the current thread as alooper.
 72     * This gives you a chance to create handlers that then reference
 73     * this looper, before actually starting the loop. Be sure to call
 74     * {@link #loop()} after calling this method, and end it by calling
 75     * {@link #quit()}.
 76     */
 77   public static void prepare() {
 78       prepare(true);
 79    }
 80
 81   private static void prepare(boolean quitAllowed) {
 82       if (sThreadLocal.get() != null) {
 83           throw new RuntimeException("Only one Looper may be created perthread");
 84       }
 85       sThreadLocal.set(new Looper(quitAllowed));
 86    }


78行,呼叫prepare(true),引數true表示允許該Looper退出迴圈。UI執行緒也有Looper,而UI執行緒的Looper是不允許退出的。

82行和85行分別呼叫了sThreadLocal的get和set成員函式。首先我們來看sThreadLocal的定義:

58    static final ThreadLocal<Looper>sThreadLocal = new ThreadLocal<Looper>();

可見,sThreadLocal是模板類ThreadLocal的例項化物件。ThreadLocal<T>模板類定義在libcore/luni/src/main/java/java/lang/ThreadLocal.java檔案中。

我們來看Android文件對ThreadLocal模板類的說明:

Implements a thread-local storage, that is, a variable forwhich each thread has its own value. All threads share the same ThreadLocalobject, buteach sees a different value when accessing it, and changes made by one threaddo not affect the other threads. The implementation supports nullvalues.

ThreadLocal物件實現了執行緒的私有資料儲存,其中儲存的資料只有本執行緒自己可以訪問,其它執行緒無法操作。ThreadLocal.set(T)設定了其中儲存的資料為T。ThreadLocal.get()取得其中儲存的資料。

這樣我們就可以理解prepare函數了。82行,如果sThreadLocal.get()返回值不為null,說明該執行緒已經有一個Looper了,每個執行緒只允許有一個Looper,所以丟擲一個異常退出。如果該執行緒還沒有Looper,則執行85行,呼叫sThreadLocal.set(newLooper(quitAllowed)),new一個Looper,儲存在sThreadLocal中。這樣,通過呼叫prepare函式,就為該執行緒建立一個Looper物件。

還有一點需要說明的是,呼叫new Looper(quitAllowed)時,會建立該執行緒的訊息佇列,來看Looper的建構函式:

220    private Looper(boolean quitAllowed) {
221        mQueue = new MessageQueue(quitAllowed);
222        mThread = Thread.currentThread();
223    }


221行,new一個MessageQueue儲存在mQueue變數中,即該執行緒的訊息佇列。

223行,取得當前執行緒儲存在mThread變數中。

這樣,再總結一次,通過呼叫Looper.prepare()函式,我們就為本執行緒建立了Looper,同時也建立了MessageQueue。

分析完Looper.prepare()函式,我們再來看Looper.loop()函式:

112    /**
113     * Run the message queue in this thread. Besure to call
114     * {@link #quit()} to end the loop.
115     */
116    public static void loop() {
117        final Looper me = myLooper();
118        if (me == null) {
119            throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");
120        }
121        final MessageQueue queue = me.mQueue;
122
123        // Make sure the identity of thisthread is that of the local process,
124        // and keep track of what that identitytoken actually is.
125        Binder.clearCallingIdentity();
126        final long ident =Binder.clearCallingIdentity();
127        LocalLog localLog = me.mLocalLog;
128        if (localLog != null) me.mStringBuilder= new StringBuilder();
129
130        for (;;) {
131            Message msg = queue.next(); //might block
132            if (msg == null) {
133                // No message indicates thatthe message queue is quitting.
134                return;
135            }
136
137            long dispatchStart = 0;
138            // This must be in a localvariable, in case a UI event sets the logger
139            Printer logging = me.mLogging;
140            if (logging != null) {
141               logging.println(">>>>> Dispatching to " +msg.target + " " +
142                        msg.callback + ":" + msg.what);
143            }
144            if (localLog != null) {
145                me.mDispatching =msg.toStringLw();
146                me.mDispatchStart =SystemClock.uptimeMillis();
147            }
148
149            msg.target.dispatchMessage(msg);
150
151            if (logging != null) {
152               logging.println("<<<<< Finished to " +msg.target + " " + msg.callback);
153            }
154            if (localLog != null) {
155                final long elapsed =SystemClock.uptimeMillis() - me.mDispatchStart;
156                final long wait =me.mDispatchStart - msg.when;
157                me.mStringBuilder.setLength(0);
158                if (elapsed >=LATENCY_THRESHOLD) {
159                   me.mStringBuilder.append("WARNING! ");
160                }
161               me.mStringBuilder.append("Wait: ")
162                                 .append(wait)
163                                .append("ms, Run: ")
164                                .append(elapsed)
165                                 .append("ms due Message")
166                                .append(me.mDispatching);
167               localLog.log(me.mStringBuilder.toString());
168                me.mDispatching = null;
169            }
170
171            // Make sure that during the courseof dispatching the
172            // identity of the thread wasn'tcorrupted.
173            final long newIdent =Binder.clearCallingIdentity();
174            if (ident != newIdent) {
175                Log.wtf(TAG, "Threadidentity changed from 0x"
176                        +Long.toHexString(ident) + " to 0x"
177                        +Long.toHexString(newIdent) + " while dispatching to "
178                        +msg.target.getClass().getName() + " "
179                        + msg.callback + "what=" + msg.what);
180            }
181            msg.recycle();
182        }
183    }


117-127行,準備工作,取得本執行緒的Looper和MessageQueue等元素。

130-182行,這個for迴圈即完成迴圈取出訊息,分發訊息的工作。

131行,呼叫MessageQueue.next()函式,從MessageQueue中取得一個Message。MessageQueue.next()函式可能會阻塞,其返回值只有兩種可能,一是返回取得的Message,或者返回null。如果返回null,表示退出訊息迴圈。

149行,呼叫msg.target.dispatchMessage(msg);完成訊息的分發。

Message.target是Handler物件,所以149行,就是呼叫Message對應的Handler物件的dispatchMessage函式。現在問題是Message對應的Handler是怎樣指定的。

回憶一下上一篇文章《Android架構分析之Android訊息理機制(一)》,我們傳送一個Message的過程是先new一個Message,然後呼叫Handler.sendMessage函式將Message插入本執行緒訊息佇列的尾部。過程很簡單,所以,我們有理由相信,很可能是在Handler.sendMessage函式中,為Message.target指定了對應的Handler。

在分析Handler之前,我們要再次理解Android的訊息處理流程:

1、      Handler通過Handler.sendMessage函式,將Message傳送到本執行緒的MessageQueue中。

2、      本執行緒的Looper不斷迴圈讀取本執行緒的MessageQueue,從中取出下一條Message,然後呼叫Message.target.dispatchMessage函式,將訊息傳送到對應的處理函式。而其中一個可能的處理函式就是Handler.handleMessage,應用程式一般會過載實現這個函式。

下面我們來看Handler的實現,其類定義在frameworks/base/core/java/android/os/Handler.java檔案中。

首先我們來看Android對Handler的說明:

A Handler allows you tosend and process andRunnable objects associated with a thread's . EachHandler instance is associated with a single thread and that thread's messagequeue. When you create a new Handler, it is bound to the thread / message queueof the thread that is creating it -- from that point on, it will delivermessages and runnables to that message queue and execute them as they come outof the message queue.

There are two main usesfor a Handler: (1) to schedule messages and runnables to be executed as somepoint in the future; and (2) to enqueue an action to be performed on adifferent thread than your own.

Scheduling messages isaccomplished with the ,,and methods.The post versionsallow you to enqueue Runnable objects to be called by the message queue when theyare received; the sendMessage versions allow you to enqueue a objectcontaining a bundle of data that will be processed by the Handler's method(requiring that you implement a subclass of Handler).

When posting or sendingto a Handler, you can either allow the item to be processed as soon as themessage queue is ready to do so, or specify a delay before it gets processed orabsolute time for it to be processed. The latter two allow you to implementtimeouts, ticks, and other timing-based behavior.

When a process iscreated for your application, its main thread is dedicated to running a messagequeue that takes care of managing the top-level application objects(activities, broadcast receivers, etc) and any windows they create. You cancreate your own threads, and communicate back with the main application threadthrough a Handler. This is done by calling the same post or sendMessage methods as before, but from your newthread. The given Runnable or Message will then be scheduled in the Handler'smessage queue and processed when appropriate.

可以看到,Handler用來將Message插入到MessageQueue中,同時在適當的時候(即從MessageQueue中取出Message時),Handler也用來對訊息進行處理。

Handler提供了多個函式用於將Message插入到MessageQueue中,我們以sendMessage為例,該函式定義如下:

492    /**
493     * Pushes a message onto the end of themessage queue after all pending messages
494     * before the current time. It will bereceived in {@link #handleMessage},
495     * in the thread attached to this handler.
496     *
497     * @return Returns true if the message wassuccessfully placed in to the
498     *        message queue.  Returns false onfailure, usually because the
499     *        looper processing the message queue is exiting.
500     */
501    public final boolean sendMessage(Messagemsg)
502    {
503        return sendMessageDelayed(msg, 0);
504    }


實際上呼叫的是sendMessageDelayed函式,該函式定義如下:

549    /**
550     * Enqueue a message into the message queueafter all pending messages
551     * before (current time + delayMillis). Youwill receive it in
552     * {@link #handleMessage}, in the threadattached to this handler.
553     *
554     * @return Returns true if the message wassuccessfully placed in to the
555     *        message queue.  Returns false onfailure, usually because the
556     *        looper processing the message queue is exiting.  Note that a
557     *        result of true does not mean the message will be processed -- if
558     *        the looper is quit before the delivery time of the message
559     *        occurs then the message will be dropped.
560     */
561    public final booleansendMessageDelayed(Message msg, long delayMillis)
562    {
563        if (delayMillis < 0) {
564            delayMillis = 0;
565        }
566        return sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis);
567    }


實際上呼叫的是sendMessageAtTime函式,該函式定義如下:

569    /**
570     * Enqueue a message into the message queueafter all pending messages
571     * before the absolute time (in milliseconds)<var>uptimeMillis</var>.
572     * <b>The time-base is {@linkandroid.os.SystemClock#uptimeMillis}.</b>
573     * You will receive it in {@link#handleMessage}, in the thread attached
574     * to this handler.
575     *
576     * @param uptimeMillis The absolute time atwhich the message should be
577     *        delivered, using the
578     *        {@link android.os.SystemClock#uptimeMillis} time-base.
579     *
580     * @return Returns true if the message wassuccessfully placed in to the
581     *        message queue.  Returns false onfailure, usually because the
582     *        looper processing the message queue is exiting.  Note that a
583     *        result of true does not mean the message will be processed -- if
584     *        the looper is quit before thedelivery time of the message
585     *        occurs then the message will be dropped.
586     */
587    public boolean sendMessageAtTime(Messagemsg, long uptimeMillis) {
588        MessageQueue queue = mQueue;
589        if (queue == null) {
590            RuntimeException e = newRuntimeException(
591                    this + "sendMessageAtTime() called with no mQueue");
592            Log.w("Looper",e.getMessage(), e);
593            return false;
594        }
595        return enqueueMessage(queue, msg,uptimeMillis);
596    }


實際上是呼叫enqueueMessage函式,將Message插入到MessageQueue中。該函式定義如下:

621    private boolean enqueueMessage(MessageQueuequeue, Message msg, long uptimeMillis) {
622        msg.target = this;
623        if (mAsynchronous) {
624            msg.setAsynchronous(true);
625        }
626        return queue.enqueueMessage(msg,uptimeMillis);
627    }


可以看到,622行,將Message.target設定為this,即當前Handler。這就解釋了我們前面分析Looper時提出的問題:Looper從訊息佇列中取出了Message,然後呼叫Message.target.dispatchMessage函式,將訊息傳送到對應的處理函式,Message.target是什麼時候被設定的?Message.target在使用Handler.sendMessage函式將訊息傳送到MessageQueue時,就被設定為當前Handler。

626行,呼叫MessageQueue.enqueueMessage將Message插入到MessageQueue中。

以上我們知道了怎樣通過Handler.sendMessage等函式將Message插入到MessageQueue中。並且Looper在迴圈從MessageQueue中取出一個Message後,會呼叫Message.target.dispatchMessage函式,即Message對應的Handler的dispatchMessage函式。下面我們就來看一下Handler.dispatchMessage函式,其定義如下:

90    /**
 91     *Handle system messages here.
 92    */
 93   public void dispatchMessage(Message msg) {
 94       if (msg.callback != null) {
 95           handleCallback(msg);
 96       } else {
 97           if (mCallback != null) {
 98                if(mCallback.handleMessage(msg)) {
 99                    return;
100                }
101            }
102            handleMessage(msg);
103        }
104    }


可以看到,如果指定了Message.callback,則呼叫Handler.handleCallback函式,其定義如下:

732    private static void handleCallback(Messagemessage) {
733        message.callback.run();
734    }


Message.callback是Runnable類的物件,733行,呼叫Message.callback.run函式開始執行該Runnable物件。

如果Message.callback為null,則呼叫Handler.handleMessage函式,這也是我們在應用程式中建立Handler物件時,需要過載實現的handleMessage函式。

需要注意的是,如果mCallback不為null,還需要先呼叫mCallback.handleMessage函式,如果該函式返回非0值,就不再呼叫Handler.handleMessage函數了。mCallback定義如下:

738    final Callback mCallback;


可見,它是Callback介面型別:

73    /**
 74     *Callback interface you can use when instantiating a Handler to avoid
 75     *having to implement your own subclass of Handler.
 76     *
 77     *@param msg A {@link android.os.Message Message} object
 78     *@return True if no further handling is desired
 79    */
 80   public interface Callback {
 81       public boolean handleMessage(Message msg);
 82    }


分析到這裡,我們就能理解Android的訊息處理機制了。對於一個要處理訊息的執行緒,它要呼叫Looper.prepare()建立當前執行緒的Looper物件,同時也建立了當前執行緒的MessageQueue物件,然後建立當前執行緒的Handler物件,過載Handler.handleMessage函式用於對訊息進行處理,最後呼叫Looper.loop函式,迴圈從MessageQueue中取Message並分發處理。

在本文中,我們分析了一個普通執行緒如何處理Message,而對於Android的UI執行緒,與普通執行緒處理Message的基本原理是一樣的,但處理起來又有一定區別,下一篇文章中,我們將來分析UI執行緒是如何處理Message的。