android學習13#--Handler消息傳遞機制

本文一點一點的把與handler相關的知識點都引了出來,盡最大努力把這個機制講清楚。

爲什麼android要求子線程通過Handler來更新UI

我們先來看看官網[https://developer.android.com/training/multiple-threads/communicate-ui.html#Handler]的這段文字:
Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren’t running on your UI thread, they don’t have access to UI objects. To move data from a background thread to the UI thread, use a Handler that’s running on the UI thread.
總的意思是:app啓動總是會創建一個單獨主線程UI線程,約定組件操作只能再UI線程中完成,其它線程需要操作組件時,需藉助Handler來完成。

Handler功能

  1. [https://developer.android.com/reference/android/os/Handler.html]上的文字:
    A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
    基本意思:Handler允許你發送和處理與線程的MessageQueue有關的message和runnable對象,一個handler對象只能和一個thread有關,每一個handler的創建一定會綁定一個thread或message queue;從那時起,創建handler的線程將會發送message和runnable到message queue,而主線程會從message queue中取出來處理。
  2. There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
    Handler主要有兩個用途:(1) message和runnable會在將來的某個時間點執行;(2) 需要再另一個線程中添加入隊動作,不是本線程。
  3. Handler是線程管理框架的一部分,主要是接收子線程發送的數據,並用此數據配合主線程更新UI,用來跟UI主線程交互用。
    • 比如在子線程處理一些耗時的操作,然後子線程用handler發送一個message給主線程UI線程的handler來接收、處理該消息更新UI,這樣以避免直接在UI主線程中處理事務導致影響UI主線程的其他處理工作,Android提供了Handler作爲主線程和子線程的紐帶;
    • 也可以將handler對象傳給其他進程,以便在其他進程中通過handler給你發送事件;
    • 還可以通過handler的延時發送message,可以延時處理一些事務的處理。

創建Handle

  1. Handler():
    Default constructor associates this handler with the Looper for the current thread.
  2. Handler(Handler.Callback callback):
    Constructor associates this handler with the Looper for the current thread and takes a callback interface in which you can handle messages.
  3. Handler(Looper looper):
    Use the provided Looper instead of the default one.
  4. Handler(Looper looper, Handler.Callback callback):
    Use the provided Looper instead of the default one and take a callback interface in which to handle messages.

創建looper

上面的4個構造方法中,會發現每一個構造方法都會有一個looper類。先來說說looper類
API中提到:This class contains the code required to set up and manage an event loop based on MessageQueue
意思是Looper類包含了需要建立和管理一個基於MessageQueue的事件代碼

[https://developer.android.com/reference/android/os/Looper.html]上講:
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Looper類是用於處理線程的消息隊列的這麼一個類。線程默認情況下不帶有消息循環;在線程中調用prepare()方法創造looper對象,然後調用loop()方法啓動消息處理,直到loop結束。
先來看看looper類的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) {//這個判斷證了一個線程中只有一個Looper實例
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

上面的prepare()方法保證了每一個線程最多隻有一個looper對象,注意prepare()是一個靜態函數,再來看看looper構造方法源碼:

    /**
     * Return the {@link MessageQueue} object associated with the current
     * thread.  This must be called from a thread running a Looper, or a
     * NullPointerException will be thrown.
     */
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

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

這裏會創建一個MessageQueue對象。因此在初始化Looper對象時,會創建一個與之關聯的MessageQueue,這個MessageQueue負責管理消息。
再來看看啓動消息管理的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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

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

loop()方法是採用一個死循環的方式遍歷MessageQueue中的message,調用調用msg.target.dispatchMessage(msg)將消息分發給對應的Handler進行處理。

再回到Handler構造函數

我們發現第一個跟第二個構造函數並沒有傳遞Looper,查看源碼,這兩個構造函數其實調用的是下面這個函數,源碼如下:

    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;
    }

你會發現最終會調用mLooper = Looper.myLooper();來獲取當前線程綁定的Looper對象。同時通過這個Looper對象獲取對應的MessageQueue對象。
第三個跟第四個構造函數實際調用的是如下函數:

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

looper對象是外面傳遞進來的,這個不難理解。
再來看看第二個和第四個構造函數有一個參數Callback callback。查看Callback對象,發現它是Handler的內部接口,需要實現內部的handleMessage()方法,接口源碼如下:

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

很明顯這個接口是用來處理消息的方法,該方法通常需要被重寫。重寫有兩個方法:
1. 採用回調的形式,即通過第二個或第四個初始化Handler對象。
2. 採用重寫的方法,即通過第一個或第三個初始化Handler對象。
但是實際中具體是哪一種構造方法用得多,我也不是很清楚,先了解這些原理吧,後續實踐的時候留意下。

Handler分發消息的一些方法

post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
查看源碼最後都是調用了sendMessageDelayed()方法,sendMessageDelayed()調用的是sendMessageAtTime()方法,sendMessageAtTime()最終調用的是enqueueMessage();

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

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//this就是當前的Handle對象
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

上面的幾個函數實現邏輯:把當前的Handler作爲msg的target對象,通過Handler的mQueue對象的enqueueMessage()方法將消息放入MessageQueue的消息隊列中。

總結

到了這裏,總結一下,前面我依次從Handler入手,引入了MessageQueue,由Handler構造函數引入Looper,再由Looper引入MessageQueue,然後又回到Handler構造函數,哈哈,這條路是不是很清晰。
1. Looper:每個線程只有一個looper,負責新建和管理MessageQueue,負責將message從MessageQueue中取出來分發給對應的Handler。
2. MessageQueue:由looper創建並管理。
3. Handler:把消息發送給Looper管理的MessageQueue,並負責處理Looper分發給他的消息。
再來梳理下線程中使用Handler的步驟:
1. 調用Looper的靜態prepare()方法爲當前線程創建一個looper對象,創建looper對象是,系統會自動創建一個與之配套的MessageQueue。
2. 當前線程有了looper對象和MessageQueue後,就可以創建Handler了,記得根據需求重寫handleMessage()方法
3. 最後調用Looper的靜態loop()函數啓動Looper。

需要特別說明的是,我們的主線程UI Thread在app系統一啓動的時候就會創建,同時也會創建一個looper對象,因此再主線程中,我們可直接創建Handler對象,而不需要按照上面的所說的先調用Looper.prepare()等後再創建Handler對象

引用:

http://blog.csdn.net/amazing7/article/details/51424038
http://www.cnblogs.com/angeldevil/p/3340644.html
http://blog.csdn.net/goodlixueyong/article/details/50831958
http://www.jb51.net/article/37465.htm
http://blog.csdn.net/army_jun/article/details/51729351
http://blog.csdn.net/heng615975867/article/details/9194219
http://blog.jobbole.com/73267/
http://www.cnblogs.com/colder/p/3791299.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章