Android異步消息處理機制 Handler、Looper、Message

感覺讓我受益很多的一句話: 好記性不如爛筆頭。

Handler、Looper、Message三者簡介

Handler簡介

Handler 爲Android操作系統中的線程通信工具,它主要由兩個作用:

  • (1)、安排消息或Runnable 在某個主線程中某個地方執行
  • (2)、安排一個動作在另外的線程中執行。

Looper簡介

Looper類用來爲線程開啓一個消息循環,作用是可以循環的從消息隊列讀取消息,所以Looper實際上就是消息隊列+消息循環的封裝。每個線程只能對應一個Looper,除主線程外,Android中的線程默認是沒有開啓Looper的。

Message對象簡介

Message對象攜帶數據,通常它用arg1,arg2來傳遞消息,當然它還可以有obj參數,可以攜帶Bundle數據。它的特點是系統性能消耗非常少。

總體簡介:

Handler 、 Looper 、Message 這三者都與Android異步消息處理線程相關的概念。那麼什麼叫異步消息處理線程呢?

異步消息處理線程啓動後會進入一個無限的循環體之中,每循環一次,從其內部的消息隊列中取出一個消息,然後回調相應的消息處理函數,執行完成一個消息後則繼續循環。若消息隊列爲空,線程則會阻塞等待。

其實Looper負責的就是創建一個MessageQueue,然後進入一個無限循環體不斷從該MessageQueue中讀取消息,而消息的創建者就是一個或多個Handler 。

下面則由我們一起進入源碼,進行簡單的瞭解一下:

刨根問底,深入源碼簡單探究

1、Looper源碼查看

對於Looper主要是prepare()和loop()兩個方法。

(1)首先prepare()方法

 /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

sThreadLocal是一個ThreadLocal對象,可以在一個線程中存儲變量。可以看到,在第12行,將一個Looper的實例放入了ThreadLocal,並且在放入之前行判斷了sThreadLocal是否爲null,否則拋出異常。這也就說明了Looper.prepare()方法不能被調用兩次,同時也保證了一個線程中只有一個Looper實例。

Looper的構造方法

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);

        mThread = Thread.currentThread();
    }

在構造方法中,創建了一個MessageQueue(消息隊列)。MessageQueue變量已聲明爲:final MessageQueue mQueue;

(2)然後看loop()方法:


    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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;

        // 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();

        for (;;) {
            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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            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.recycleUnchecked();
        }
    }

     /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
      public static @Nullable Looper myLooper() {
          return sThreadLocal.get();
   }

myLooper()方法直接返回了sThreadLocal存儲的Looper實例,如果me爲null則拋出異常,也就是說looper方法必須在prepare方法之後運行。

第8行:拿到該looper實例中的mQueue(消息隊列)

for (;;){}方法:就進入了我們所說的無限循環。

Message msg = queue.next();取出一條消息,如果沒有消息則阻塞。

取出消息之後:使用調用msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。Msg的target是什麼呢?其實就是handler對象。

msg.recycleUnchecked();釋放消息佔據的資源。

Looper主要作用:

  • 1、 與當前線程綁定,保證一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue。
  • 2、 loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。

下面—–>>>>Handler登場了。

2、Handler源碼賞析

使用Handler之前,我們都是初始化一個實例,比如用於更新UI線程,我們會在聲明的時候直接初始化,或者在onCreate中初始化Handler實例。所以我們首先看Handler的構造方法,看其如何與MessageQueue聯繫上的,它在子線程中發送的消息(一般發送消息都在非UI線程)怎麼發送到MessageQueue中的。


     /**
     * Use the {@link Looper} for the current thread with the specified callback interface
     * and set whether the handler should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

通過Looper.myLooper()獲取了當前線程保存的Looper實例,

然後在mQueue = mLooper.mQueue;又獲取了這個Looper實例中保存的MessageQueue(消息隊列),這樣就保證了handler的實例與我們Looper實例中MessageQueue關聯上了。

然後看我們最常用的sendMessage方法:

    /*此段將註釋去掉,太多,看方法名我們也能大致的瞭解*/

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }


    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }


    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }


    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }


    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }


    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

最後的最後都去調用了sendMessageAtTime,在此方法內部有直接獲取該Handler的中的Looper的MessageQueue然後調用了enqueueMessage方法,方法如下:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

首先爲msg.target賦值爲this,也就是把當前的handler作爲msg的target屬性。最終會調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會保存到消息隊列中去。

現在已經很清楚了Looper會調用prepare()和loop()方法,在當前執行的線程中保存一個Looper實例,

這個實例會保存一個MessageQueue對象,然後當前線程進入一個無限循環中去,不斷從MessageQueue中讀取Handler發來的消息。

然後再回調創建這個消息的handler中的dispathMessage方法,下面我們趕快去看一看這個方法:

    /**
     * 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);
        }
    }

調用了handleMessage方法,下面我們去看這個方法:


    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

這個方法是不是很熟悉啊?

上段熟悉的代碼塊:

    Handler mHandler=new Handler(){
     @Override
      public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what){
            case 1:
            Log.e("看源碼長知識","前面標識老大哥說得對!");
                break;
            default:
            Log.e("看源碼長知識","我就什麼也不做");
                break;
              }
         }
    };

創建Handler要重寫的那個方法,自己處理事件和更新UI的那個方法。恍然大悟!!!

讓我們首先總結一下這個流程

  • 1、首先Looper.prepare()在本線程中保存一個Looper實例,然後該實例中保存一個MessageQueue對象;因爲Looper.prepare()在一個線程中只能調用一次,所以MessageQueue在一個線程中只會存在一個。
  • 2、Looper.loop()會讓當前線程進入一個無限循環,不端從MessageQueue的實例中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。
  • 3、Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue關聯。
  • 4、Handler的sendMessage方法,會給msg的target賦值爲handler自身,然後加入MessageQueue中。
  • 5、在構造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。
  • 總結完成,大家可能還會問,那麼在Activity中,我們並沒有顯示的調用Looper.prepare()和Looper.loop()方法,爲啥Handler可以成功創建呢,這是因爲在Activity的啓動代碼中,已經在當前UI線程調用了Looper.prepare()和Looper.loop()方法。

增添Handler post方法解析

Handler的post方法創建的線程和UI線程有什麼關係?

有時候爲了方便,我們會直接寫如下代碼:

mHandler.post(new Runnable()  
        {  
            @Override  
            public void run()  
            {  
                Log.e("TAG", Thread.currentThread().getName());  
                mTxt.setText("yoxi");  
            }  
        });  

然後run方法中可以寫更新UI的代碼,其實這個Runnable並沒有創建什麼線程,而是發送了一條消息,下面看源碼:

    /**
     * Causes the Runnable r to be added to the message queue.
     * The runnable will be run on the thread to which this handler is 
     * attached. 
     *  
     * @param r The Runnable that will be executed.
     * 
     * @return Returns true if the Runnable was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

getPostMessage(r)源碼

   private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

可以看到,在getPostMessage中,得到了一個Message對象,然後將我們創建的Runable對象作爲callback屬性,賦值給了此message.

注:產生一個Message對象,可以new ,也可以使用Message.obtain()方法;

兩者都可以,但是更建議使用obtain方法,因爲Message內部維護了一個Message池用於Message的複用,避免使用new 重新分配內存。

public final boolean sendMessageDelayed(Message msg, long delayMillis)  
   {  
       if (delayMillis < 0) {  
           delayMillis = 0;  
       }  
       return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
   }  

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
       MessageQueue queue = mQueue;  
       if (queue == null) {  
           RuntimeException e = new RuntimeException(  
                   this + " sendMessageAtTime() called with no mQueue");  
           Log.w("Looper", e.getMessage(), e);  
           return false;  
       }  
       return enqueueMessage(queue, msg, uptimeMillis);  
   }  

最終和handler.sendMessage一樣,調用了sendMessageAtTime,然後調用了enqueueMessage方法,給msg.target賦值爲handler,最終加入MessagQueue.

可以看到,這裏msg的callback和target都有值,那麼會執行哪個呢?

其實上面已經貼過代碼,就是dispatchMessage方法:


    /**
     * 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);
        }
    }

如果msg.callback不爲null,則執行callback回調,也就是我們的Runnable對象。

好了,關於Looper , Handler , Message 這三者關係上面已經敘述的非常清楚了。

洋神的圖

後話,小小的提醒

其實Handler不僅可以更新UI,你完全可以在一個子線程中去創建一個Handler,然後使用這個handler實例在任何其他線程中發送消息,最終處理消息的代碼都會在你創建Handler實例的線程中運行。

    new Thread()  
        {  
            private Handler handler;  
            public void run()  
            {  

                Looper.prepare();  //這句話必須要加

                handler = new Handler()  
                {  
                    public void handleMessage(android.os.Message msg)  
                    {  
                        Log.e("三生三世十里桃花",Thread.currentThread().getName());  
                    };  
                };

Android不僅給我們提供了異步消息處理機制讓我們更好的完成UI的更新,其實也爲我們提供了異步消息處理機制代碼的參考,不僅能夠知道原理,最好還可以將此設計用到其他的非Android項目中去.

發佈了40 篇原創文章 · 獲贊 234 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章