Handler post(Runnable runnable)和sendMessage(Message msg)的區別比較

yangxi_001發表於2016-12-23

先上Handler的原始碼看一下:

[java] view plain copy
  1. /** 
  2.      * Default constructor associates this handler with the {@link Looper} for the 
  3.      * current thread. 
  4.      * 
  5.      * If this thread does not have a looper, this handler won't be able to receive messages 
  6.      * so an exception is thrown. 
  7.      */  
  8.     public Handler() {  
  9.         this(nullfalse);  
  10.     }  
  11.   
  12.     /** 
  13.      * Constructor associates this handler with the {@link Looper} for the 
  14.      * current thread and takes a callback interface in which you can handle 
  15.      * messages. 
  16.      * 
  17.      * If this thread does not have a looper, this handler won't be able to receive messages 
  18.      * so an exception is thrown. 
  19.      * 
  20.      * @param callback The callback interface in which to handle messages, or null. 
  21.      */  
  22.     public Handler(Callback callback) {  
  23.         this(callback, false);  
  24.     }  
  25.   
  26.     /** 
  27.      * Use the provided {@link Looper} instead of the default one. 
  28.      * 
  29.      * @param looper The looper, must not be null. 
  30.      */  
  31.     public Handler(Looper looper) {  
  32.         this(looper, nullfalse);  
  33.     }  
  34.   
  35.     /** 
  36.      * Use the provided {@link Looper} instead of the default one and take a callback 
  37.      * interface in which to handle messages. 
  38.      * 
  39.      * @param looper The looper, must not be null. 
  40.      * @param callback The callback interface in which to handle messages, or null. 
  41.      */  
  42.     public Handler(Looper looper, Callback callback) {  
  43.         this(looper, callback, false);  
  44.     }  
  45.   
  46.     /** 
  47.      * Use the {@link Looper} for the current thread 
  48.      * and set whether the handler should be asynchronous. 
  49.      * 
  50.      * Handlers are synchronous by default unless this constructor is used to make 
  51.      * one that is strictly asynchronous. 
  52.      * 
  53.      * Asynchronous messages represent interrupts or events that do not require global ordering 
  54.      * with respect to synchronous messages.  Asynchronous messages are not subject to 
  55.      * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. 
  56.      * 
  57.      * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for 
  58.      * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. 
  59.      * 
  60.      * @hide 
  61.      */  
  62.     public Handler(boolean async) {  
  63.         this(null, async);  
  64.     }  
  65.   
  66.     /** 
  67.      * Use the {@link Looper} for the current thread with the specified callback interface 
  68.      * and set whether the handler should be asynchronous. 
  69.      * 
  70.      * Handlers are synchronous by default unless this constructor is used to make 
  71.      * one that is strictly asynchronous. 
  72.      * 
  73.      * Asynchronous messages represent interrupts or events that do not require global ordering 
  74.      * with respect to synchronous messages.  Asynchronous messages are not subject to 
  75.      * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. 
  76.      * 
  77.      * @param callback The callback interface in which to handle messages, or null. 
  78.      * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for 
  79.      * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. 
  80.      * 
  81.      * @hide 
  82.      */  
  83.     public Handler(Callback callback, boolean async) {  
  84.         if (FIND_POTENTIAL_LEAKS) {  
  85.             final Class<? extends Handler> klass = getClass();  
  86.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  87.                     (klass.getModifiers() & Modifier.STATIC) == 0) {  
  88.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  89.                     klass.getCanonicalName());  
  90.             }  
  91.         }  
  92.   
  93.         mLooper = Looper.myLooper();  
  94.         if (mLooper == null) {  
  95.             throw new RuntimeException(  
  96.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  97.         }  
  98.         mQueue = mLooper.mQueue;  
  99.         mCallback = callback;  
  100.         mAsynchronous = async;  
  101.     }  
  102.   
  103.     /** 
  104.      * Use the provided {@link Looper} instead of the default one and take a callback 
  105.      * interface in which to handle messages.  Also set whether the handler 
  106.      * should be asynchronous. 
  107.      * 
  108.      * Handlers are synchronous by default unless this constructor is used to make 
  109.      * one that is strictly asynchronous. 
  110.      * 
  111.      * Asynchronous messages represent interrupts or events that do not require global ordering 
  112.      * with respect to synchronous messages.  Asynchronous messages are not subject to 
  113.      * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. 
  114.      * 
  115.      * @param looper The looper, must not be null. 
  116.      * @param callback The callback interface in which to handle messages, or null. 
  117.      * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for 
  118.      * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. 
  119.      * 
  120.      * @hide 
  121.      */  
  122.     public Handler(Looper looper, Callback callback, boolean async) {  
  123.         mLooper = looper;  
  124.         mQueue = looper.mQueue;  
  125.         mCallback = callback;  
  126.         mAsynchronous = async;  
  127.     }  

無聊的原始碼copy,可以看到最終都是呼叫的最後一個建構函式,Android原始碼裡面到處都是這種寫法,接下來我們要講的sendMessage也是這種寫法。這樣寫的好處是,我們不用關心過多的引數,靈活適配各種不同的需求。來看下初始化的三個屬性,Looper:上面有說,Handler與建立它的執行緒的訊息佇列是繫結的,實際上Handler收發訊息的三要素Handler、MessageQueue、Looper,是缺一不可的,執行緒必須有Looper,才能從MessageQueue裡把訊息取出來,所以一定要建立Looper才能使用Handler,剛剛的例子我們並沒有看到Looper建立,而我們之所以能夠在Activity裡直接使用Handler是因為主執行緒,也叫UI執行緒、ActivityThread被建立的時候會初始化一個Looper。所以我們能夠在UI執行緒裡直接使用Looper(因為更新UI使用的多,而且Android整個視窗的UI更新都是用的Handler機制)。Callback:介面,回撥用來寫收到訊息後處理訊息的程式碼。async:傳給訊息的一個boolean型變數,是否需要同步。

直接來看我們最熟悉的sendMessage的方法:

[java] view plain copy
  1. public final boolean sendMessage(Message msg)  
  2.  {  
  3.      return sendMessageDelayed(msg, 0);  
  4.  }  

最普通的傳送訊息,其內部使用了延時傳送,延時設為0. 這是Android裡面常用的模式,這樣做的好處是當你不需要傳引數的時候可以直接使用無參的,使用方便,而且內部減少

了程式碼量,避免再重複寫一個傳送訊息的實現。

   

[java] view plain copy
  1. /** 
  2.      * Sends a Message containing only the what value. 
  3.      *   
  4.      * @return Returns true if the message was successfully placed in to the  
  5.      *         message queue.  Returns false on failure, usually because the 
  6.      *         looper processing the message queue is exiting. 
  7.      */  
  8.     public final boolean sendEmptyMessage(int what)  
  9.     {  
  10.         return sendEmptyMessageDelayed(what, 0);  
  11.     }  

傳送一個空訊息只需要傳遞一個訊號,用訊息型別就足夠攜帶我們要傳遞的資訊。同樣呼叫了它的延時傳送方法,延時為0.

[java] view plain copy
  1. /** 
  2.  * Sends a Message containing only the what value, to be delivered 
  3.  * after the specified amount of time elapses. 
  4.  * @see #sendMessageDelayed(android.os.Message, long)  
  5.  *  
  6.  * @return Returns true if the message was successfully placed in to the  
  7.  *         message queue.  Returns false on failure, usually because the 
  8.  *         looper processing the message queue is exiting. 
  9.  */  
  10. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {  
  11.     Message msg = Message.obtain();  
  12.     msg.what = what;  
  13.     return sendMessageDelayed(msg, delayMillis);  
  14. }  

我們可以看到,系統同樣是通過傳送Message物件來實現的傳送空訊息。這個物件值承載了我們設定的What資訊。它最終調的也是 sendMessageDelayed(msg, delayMillis);延時傳送一個訊息。這裡用了 Message.obtain();這是一種單例模式,避免每傳送一個訊息就new出一個物件,如果這樣記憶體佔用會很高,而且沒有必要。在講Message的時候會講到。

 

[java] view plain copy
  1. /** 
  2.   * Sends a Message containing only the what value, to be delivered  
  3.   * at a specific time. 
  4.   * @see #sendMessageAtTime(android.os.Message, long) 
  5.   *   
  6.   * @return Returns true if the message was successfully placed in to the  
  7.   *         message queue.  Returns false on failure, usually because the 
  8.   *         looper processing the message queue is exiting. 
  9.   */  
  10.  public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {  
  11.      Message msg = Message.obtain();  
  12.      msg.what = what;  
  13.      return sendMessageAtTime(msg, uptimeMillis);  
  14.  }  

這是一個定時在某個確定的時刻傳送空訊息的方法,內部同樣呼叫了傳送訊息對應的方法。如果我們猜想的話,這個確定時刻傳送同樣可以用延時傳送訊息的方法來實現,只需要計算確定的那個時刻和當前時間的差值,然後把這個差值設為延時引數即可。後面來驗證我們的猜想是否正確。

[java] view plain copy
  1. /** 
  2.      * Enqueue a message into the message queue after all pending messages 
  3.      * before (current time + delayMillis). You will receive it in 
  4.      * {@link #handleMessage}, in the thread attached to this handler. 
  5.      *   
  6.      * @return Returns true if the message was successfully placed in to the  
  7.      *         message queue.  Returns false on failure, usually because the 
  8.      *         looper processing the message queue is exiting.  Note that a 
  9.      *         result of true does not mean the message will be processed -- if 
  10.      *         the looper is quit before the delivery time of the message 
  11.      *         occurs then the message will be dropped. 
  12.      */  
  13.     public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  14.     {  
  15.         if (delayMillis < 0) {  
  16.             delayMillis = 0;  
  17.         }  
  18.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  19.     }  

看了這個方法,我們突然有一種被耍的趕腳,原來不是定時傳送調了延時傳送,而是延時傳送內部調了定時傳送,哈哈,這樣也可以解釋,因為他們本身就可以通過當前時間相互來轉化。這裡把當前時間加上延時時間,來計算要傳送訊息的時刻,最終用定時傳送來實現。

[java] view plain copy
  1. /** 
  2.     * Enqueue a message into the message queue after all pending messages 
  3.     * before the absolute time (in milliseconds) <var>uptimeMillis</var>. 
  4.     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> 
  5.     * Time spent in deep sleep will add an additional delay to execution. 
  6.     * You will receive it in {@link #handleMessage}, in the thread attached 
  7.     * to this handler. 
  8.     *  
  9.     * @param uptimeMillis The absolute time at which the message should be 
  10.     *         delivered, using the 
  11.     *         {@link android.os.SystemClock#uptimeMillis} time-base. 
  12.     *          
  13.     * @return Returns true if the message was successfully placed in to the  
  14.     *         message queue.  Returns false on failure, usually because the 
  15.     *         looper processing the message queue is exiting.  Note that a 
  16.     *         result of true does not mean the message will be processed -- if 
  17.     *         the looper is quit before the delivery time of the message 
  18.     *         occurs then the message will be dropped. 
  19.     */  
  20.    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  21.        MessageQueue queue = mQueue;  
  22.        if (queue == null) {  
  23.            RuntimeException e = new RuntimeException(  
  24.                    this + " sendMessageAtTime() called with no mQueue");  
  25.            Log.w("Looper", e.getMessage(), e);  
  26.            return false;  
  27.        }  
  28.        return enqueueMessage(queue, msg, uptimeMillis);  
  29.    }  

可以看到這個才是真正的傳送了一條訊息的方法,上面的所有傳送訊息的方法最終都是要調這個方法。在傳送訊息的最後又呼叫了這個enqueueMessage(queue, msg, uptimeMillis);從名字看,我們大概可以猜到,這裡應該是把訊息和訊息確切的傳送時間,塞到訊息佇列裡面。稍有開發經驗的都知道,把訊息塞給佇列這種事理論上應該交個MessageQueue來提供一個方法比較合理,Android為什麼這麼搞呢?我們帶著這個疑問來看Handler提供的這個方法:

[java] view plain copy
  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  2.        msg.target = this;  
  3.        if (mAsynchronous) {  
  4.            msg.setAsynchronous(true);  
  5.        }  
  6.        return queue.enqueueMessage(msg, uptimeMillis);  
  7.    }  

這裡可以看到這裡這個方法並沒有做什麼事,只是給Message的target屬性賦值,表示這個訊息的接收者是自己,這不就是Handler自己發訊息給自己的判斷標識麼。。。mAsynchronous這個值是表示是否是同步的,是在Handler構造方法裡傳進來的。最後又把這個屬性塞給了Message,讓Message自己處理是否同步的問題。後面調了MessageQueue的enqueueMessage方法,到這裡Handler訊息的傳送過程也就結束了。(多說一句,這裡傳的時間,其實是用來排序的,Message裡面有一個long型的屬性when,就是Message的時間標籤,最終這裡傳入的uptimeillis會賦值給msg的when屬性,讓MessageQueue用來根據時間對所有插入Message進行排隊,構成一個單連結串列。存放訊息和迴圈用。為什麼用連結串列,資料不停的再插入刪除什麼的操作,所以用連結串列,Android真屌,我平時都注意不到這些小細節,自己上去就亂搞。在MessageQueue的原始碼裡可以看到這個過程,這裡就不再講述了,因為我們平常也不和MessageQueue直接打交道)

[java] view plain copy
  1. /** 
  2.     * Enqueue a message at the front of the message queue, to be processed on 
  3.     * the next iteration of the message loop.  You will receive it in 
  4.     * {@link #handleMessage}, in the thread attached to this handler. 
  5.     * <b>This method is only for use in very special circumstances -- it 
  6.     * can easily starve the message queue, cause ordering problems, or have 
  7.     * other unexpected side-effects.</b> 
  8.     *   
  9.     * @return Returns true if the message was successfully placed in to the  
  10.     *         message queue.  Returns false on failure, usually because the 
  11.     *         looper processing the message queue is exiting. 
  12.     */  
  13.    public final boolean sendMessageAtFrontOfQueue(Message msg) {  
  14.        MessageQueue queue = mQueue;  
  15.        if (queue == null) {  
  16.            RuntimeException e = new RuntimeException(  
  17.                this + " sendMessageAtTime() called with no mQueue");  
  18.            Log.w("Looper", e.getMessage(), e);  
  19.            return false;  
  20.        }  
  21.        return enqueueMessage(queue, msg, 0);  
  22.    }  

和這個和上面的方法基本一樣就是設定了時間為0,表示在排隊時把它塞到佇列的最前端。不多講了。

postRunnable的所有方法:

[java] view plain copy
  1. /** 
  2.      * Causes the Runnable r to be added to the message queue. 
  3.      * The runnable will be run on the thread to which this handler is  
  4.      * attached.  
  5.      *   
  6.      * @param r The Runnable that will be executed. 
  7.      *  
  8.      * @return Returns true if the Runnable was successfully placed in to the  
  9.      *         message queue.  Returns false on failure, usually because the 
  10.      *         looper processing the message queue is exiting. 
  11.      */  
  12.     public final boolean post(Runnable r)  
  13.     {  
  14.        return  sendMessageDelayed(getPostMessage(r), 0);  
  15.     }  
  16.   
  17.     /** 
  18.      * Causes the Runnable r to be added to the message queue, to be run 
  19.      * at a specific time given by <var>uptimeMillis</var>. 
  20.      * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> 
  21.      * Time spent in deep sleep will add an additional delay to execution. 
  22.      * The runnable will be run on the thread to which this handler is attached. 
  23.      * 
  24.      * @param r The Runnable that will be executed. 
  25.      * @param uptimeMillis The absolute time at which the callback should run, 
  26.      *         using the {@link android.os.SystemClock#uptimeMillis} time-base. 
  27.      *   
  28.      * @return Returns true if the Runnable was successfully placed in to the  
  29.      *         message queue.  Returns false on failure, usually because the 
  30.      *         looper processing the message queue is exiting.  Note that a 
  31.      *         result of true does not mean the Runnable will be processed -- if 
  32.      *         the looper is quit before the delivery time of the message 
  33.      *         occurs then the message will be dropped. 
  34.      */  
  35.     public final boolean postAtTime(Runnable r, long uptimeMillis)  
  36.     {  
  37.         return sendMessageAtTime(getPostMessage(r), uptimeMillis);  
  38.     }  
  39.   
  40.     /** 
  41.      * Causes the Runnable r to be added to the message queue, to be run 
  42.      * at a specific time given by <var>uptimeMillis</var>. 
  43.      * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> 
  44.      * Time spent in deep sleep will add an additional delay to execution. 
  45.      * The runnable will be run on the thread to which this handler is attached. 
  46.      * 
  47.      * @param r The Runnable that will be executed. 
  48.      * @param uptimeMillis The absolute time at which the callback should run, 
  49.      *         using the {@link android.os.SystemClock#uptimeMillis} time-base. 
  50.      *  
  51.      * @return Returns true if the Runnable was successfully placed in to the  
  52.      *         message queue.  Returns false on failure, usually because the 
  53.      *         looper processing the message queue is exiting.  Note that a 
  54.      *         result of true does not mean the Runnable will be processed -- if 
  55.      *         the looper is quit before the delivery time of the message 
  56.      *         occurs then the message will be dropped. 
  57.      *          
  58.      * @see android.os.SystemClock#uptimeMillis 
  59.      */  
  60.     public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)  
  61.     {  
  62.         return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);  
  63.     }  
  64.   
  65.     /** 
  66.      * Causes the Runnable r to be added to the message queue, to be run 
  67.      * after the specified amount of time elapses. 
  68.      * The runnable will be run on the thread to which this handler 
  69.      * is attached. 
  70.      * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> 
  71.      * Time spent in deep sleep will add an additional delay to execution. 
  72.      *   
  73.      * @param r The Runnable that will be executed. 
  74.      * @param delayMillis The delay (in milliseconds) until the Runnable 
  75.      *        will be executed. 
  76.      *         
  77.      * @return Returns true if the Runnable was successfully placed in to the  
  78.      *         message queue.  Returns false on failure, usually because the 
  79.      *         looper processing the message queue is exiting.  Note that a 
  80.      *         result of true does not mean the Runnable will be processed -- 
  81.      *         if the looper is quit before the delivery time of the message 
  82.      *         occurs then the message will be dropped. 
  83.      */  
  84.     public final boolean postDelayed(Runnable r, long delayMillis)  
  85.     {  
  86.         return sendMessageDelayed(getPostMessage(r), delayMillis);  
  87.     }  
  88.   
  89.     /** 
  90.      * Posts a message to an object that implements Runnable. 
  91.      * Causes the Runnable r to executed on the next iteration through the 
  92.      * message queue. The runnable will be run on the thread to which this 
  93.      * handler is attached. 
  94.      * <b>This method is only for use in very special circumstances -- it 
  95.      * can easily starve the message queue, cause ordering problems, or have 
  96.      * other unexpected side-effects.</b> 
  97.      *   
  98.      * @param r The Runnable that will be executed. 
  99.      *  
  100.      * @return Returns true if the message was successfully placed in to the  
  101.      *         message queue.  Returns false on failure, usually because the 
  102.      *         looper processing the message queue is exiting. 
  103.      */  
  104.     public final boolean postAtFrontOfQueue(Runnable r)  
  105.     {  
  106.         return sendMessageAtFrontOfQueue(getPostMessage(r));  
  107.     }  

這裡放一塊兒說,可以明顯看到,post系列的所有方法,立即post,定時post,延時post方法裡面都是呼叫的sedMessage系列的對應的方法。而一個明顯的標誌就是Message引數都是把Runnable物件傳給getPostMessage(r)返回了一個Message物件,如果沒猜錯,這貨又是把Runnable賦給Message裡面的一個Runnable屬性。來看這個方法:

[java] view plain copy
  1. private static Message getPostMessage(Runnable r, Object token) {  
  2.     Message m = Message.obtain();  
  3.     m.obj = token;  
  4.     m.callback = r;  
  5.     return m;  
  6. }  

就知道,果然不出所料,Message裡面有一個叫callback的Runnable屬性。這樣是不是很完美呢,無論你send一個Message,還是post一個Runnnable,最終都是send一個Message。然後我把Runnable放到Message的Runnnable屬性裡面,接到Message再拿出來,就這麼簡單。


Handler傳送訊息的過程是向訊息佇列中插入一條訊息,MessageQueue的next方法就會返回這條訊息給Looper,Looper收到這條訊息之後就開始處理了,最終訊息由looper交由Handler處理,即Handler的dispatchmessage方法會被呼叫,這是Handler就進入處理訊息的階段。

1.一個Handler只有一個佇列;

2.在呼叫Handler.post(Runnable runnable)方法時,會將runnable封裝成一個Message;

3.在佇列執行時,會判斷當前是否封裝了Runnable,如果封裝了,就直接執行Runnable,如果沒有,將當前的Message傳遞給handlerMessage(Message msg)處理;

4.Handler在例項化的時候可以設定一個callback<Handler.Callback>,callback也有一個HandlerMessage(Message msg)方法,如果步驟3中的Handler有callback,那麼呼叫的是callback的HandlerMessage(message msg),否則呼叫自身的handlerMessage(Message msg)方法。

[java] view plain copy
  1. private static dispatchMessage(Message msg){
  2.     // 檢查message的callback是否為null
  3.     if(msg.callback!=null){
  4.         handlerCallback(msg);
  5.     }
  6.     else{
  7. if(mCallback!=null){
  8. if(mCallback.handlerMessage(msg)){
  9. return;
  10. }
  11. handlerMessage(msg);
  12.      }
  13. }

首先,檢查Message的callback是否為null,不為空就通過handleCallback來處理訊息。message的callback是一個Runnbale物件,實際上就是Handler的post方法所傳遞的Runnable引數。

[java] view plain copy
  1. private static void handlerCallback(Message msg){
  2.     message.callback.run();
  3. }

其次,檢查mCallback是否為空,不為null即呼叫mCallback的handlerMessage方法來處理訊息

[java] view plain copy
  1. public interface Callback{
  2.     public boolean handlerMessage(Message msg);
  3. }

相關文章