天天看點

Handler,Looper,MessageQueue,android中的消息機制以及源碼分析(二)

本篇主要是簡單介紹消息機制模型和各元件的了解,源碼分析請見Handler,Looper,MessageQueue,android中的線程通信以及源碼分析(一)

讓我們從handler預設構造函數開始入手。預設的構造函數中實際調用的構造函數,如下圖:

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


        <strong>mLooper = Looper.myLooper();</strong>
        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是handler中一個私有成員Looper,至于Looper是什麼歡迎看上一篇博文~我們先接着往下看,在這裡調用了Looper的靜态方法myLooper()

/**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
           
Handler,Looper,MessageQueue,android中的消息機制以及源碼分析(二)

sThreadLocal是Looper中的一個final的靜态成員ThreadLocal,如下圖:

Handler,Looper,MessageQueue,android中的消息機制以及源碼分析(二)
<span style="font-size:14px;"> // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();</span>
           

下面我簡單先說一下什麼是ThreadLocal。(有關Threadlocal細節)

從字面上來看,線程本地變量。什麼意思?就是針對每一個線程都會有一個變量的線程副本存在。不了解沒關系,接着往下看。

Handler,Looper,MessageQueue,android中的消息機制以及源碼分析(二)

在Thread線程中有一個成員變量Threadlocal.Values

/**
     * Normal thread local values.
     */
    ThreadLocal.Values localValues;
           

可這又是什麼呢?

在Threadlocal.Values當中的私有成員table,用來存放變量副本。

/**
         * Map entries. Contains alternating keys (ThreadLocal) and values.
         * The length is always a power of 2.
         */
        private Object[] table;
           
Handler,Looper,MessageQueue,android中的消息機制以及源碼分析(二)

說白了,ThreadLocal就是針對每一個線程都維護了一個Values對象,Values中的table是真正存放變量副本的一張表。

這個問題現在了解起來可能比較困難,我們先放一放,接着看下面的内容。

Looper在初始化的時候都會調用它的靜态方法prepare,并且在new Looper的同時完成了消息隊列的初始化。

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        <strong>sThreadLocal.set(new Looper(quitAllowed));</strong>
    }
           
private Looper(boolean quitAllowed) {
        <strong>mQueue = new MessageQueue(quitAllowed);</strong>
        mThread = Thread.currentThread();
    }
           

在prepare裡面可以看見,首先調用threadlocal的get方法,這個方法做了什麼事情呢?是把該線程中所儲存的線程副本Looper對象取出來,如果該對象已存在,根據一個線程中隻能有一個Looper,會報出異常,否則new一個新的Looper對象并放入線程副本中。

那麼接下來,我來解釋一下什麼是線程副本。

之前有說過每一個線程中都維護了一個Looper,可是每一個線程中的Looper又互不幹擾,各司其職。在這樣的情況下,通過ThreadLocal來對每一個線程維護一個Looper執行個體,各自不互相幹擾。如果是所有的線程都共用一個Looper的話,首先資源開銷會很大,而且由于隊列可能會很長,消息分發會不及時等等。還有可能會因為并發帶來阻塞等問題。而采取在每一個有需要使用Looper的線程中,用ThreadLoca對這一Looper維護,更有效的利用了資源,效率也會更高。這就是線程副本的作用。

正常在使用handler消息機制時應該是例如下面的寫法:

new Thread(new Runnable() {
            @Override
            public void run() {

                <strong>Looper.prepare();</strong>
                Log.i("XXX", "建立handler");

               <strong>handler = new Handler()</strong> {

                };

                <strong>Looper.loop();</strong>
 
            }
        }).start();
           

首先Looper.prepare()線上程中建立Looper執行個體和消息隊列。然後執行個體化handler并完成對目前線程looper和隊列的綁定。是以我們可以看到,handler執行的線程是looper所處的線程。而預設構造函數的handler預設是和目前所線上程綁定的。當然我們也可以通過如下構造,為handler指定looper,即綁定了looper所在的線程。

public Handler(<span style="color:#ff6666;">Looper looper</span>, Callback callback, boolean async) {
        <strong>mLooper = looper</strong>;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
           

最後是Looper的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();

       <strong>for (;;)</strong> {
            M<strong>essage msg = queue.next();</strong> // 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);
            }

            <strong>msg.target.dispatchMessage</strong>(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()裡面存在一個死循環,那麼在死循環中做了哪些事情?

Message msg = queue.next(); // might block
           

取出消息隊列中的下一個Message,如果消息隊列中沒有消息則會一直阻塞,直到有消息位置才會傳回消息。如果傳回的是一個null值時,說明消息對流已經關閉,對應的Looper也應該關閉,return掉。

msg.target.dispatchMessage(msg);
           

在這一行代碼中,Message對象持有一個handler的引用target,即調用Message對應的handler的dispatch方法将消息分發出去。

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            <strong>handleCallback(msg);</strong>
        } else {
            if (mCallback != null) {
                if (<strong>mCallback.handleMessage(msg)</strong>) {
                    return;
                }
            }
            <strong>handleMessage(msg);</strong>
        }
    }
           

在dispatch中設計到一個優先級的問題。

優先執行massage中實作的callback

private static void handleCallback(Message message) {
        message.callback.run();
    }
           

其次是handler中實作的Callback接口

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

最後才是官方例子中所示,也是我們最常用的handleMessage方法

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

說完了怎麼去分發執行消息,下面我們來看看消息是如何被添加到消息隊列中的。

首先相信大家都知道,更新UI的幾種方式:

·      Activity.runOnUiThread(Runnable)

·      View.post(Runnable)

·      View.postDelayed(Runnable, long)

·      Handler

這幾種方式其實都是采用的handler中的post方法,而後調用sendMessageAtTime方法。

/**
     * Enqueue a message into the message queue after all pending messages
     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
     * Time spent in deep sleep will add an additional delay to execution.
     * You will receive it in {@link #handleMessage}, in the thread attached
     * to this handler.
     * 
     * @param uptimeMillis The absolute time at which the message should be
     *         delivered, using the
     *         {@link android.os.SystemClock#uptimeMillis} time-base.
     *         
     * @return Returns true if the message was successfully placed in to the 
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.  Note that a
     *         result of true does not mean the message will be processed -- if
     *         the looper is quit before the delivery time of the message
     *         occurs then the message will be dropped.
     */
    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 <strong>enqueueMessage(queue, msg, uptimeMillis)</strong>;
    }
           

enqueueMessage方法是将message加入隊列的方法,如下:

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

調用了消息隊列中的加入隊列方法。

隊列中的Message又是怎麼存取的呢?

Message中有一個指向下一個Message的引用next。那麼我們不難了解,Message使用了最簡單的鍊式結構,而并沒有使用像是并發進行中的競争隊列之類的結構。Message的存取是一個FIFO(先進先出。。雖然我這句是廢話。。。。大家一定都知道)隊列。

// sometimes we store linked lists of these things
    /*package*/ Message next;
           

Message實作了Parceble,用于消息傳遞

public final class Message implements Parcelable
           
public static final Parcelable.Creator<Message> CREATOR
            = new Parcelable.Creator<Message>() {
        public Message createFromParcel(Parcel source) {
            Message msg = Message.obtain();
            msg.readFromParcel(source);
            return msg;
        }
        
        public Message[] newArray(int size) {
            return new Message[size];
        }
    };
           

android線程模型是一個單線程模型,即隻有UI線程負責更新UI。因為雖然cpu可能會有多個,但是顯示隻有一個。

為了遵從單線程模型,費時操作必須在子線程中實作。這樣的話就涉及到子線程和主線程之間的通信,由此引入了消息機制。

還有就是可能會有人說以前沒有用過prepare和loop也可以直接使用handler。那是因為在主線程中,預設有主線程looper,在主線程啟動時就已經prepare并且loop了,自然就不用我們自己去實作~

下面這段代碼就是ActivityThread中的主函數:

public static void main(String[] args) {
            SamplingProfilerIntegration.start();

            // CloseGuard defaults to true and can be quite spammy.  We
            // disable it here, but selectively enable it later (via
            // StrictMode) on debug builds, but using DropBox, not logs.
            CloseGuard.setEnabled(false);

            Environment.initForCurrentUser();

            // Set the reporter for event logging in libcore
            EventLogger.setReporter(new EventLoggingReporter());

            Process.setArgV0("<pre-initialized>");

            <strong>Looper.prepareMainLooper();</strong>

            // 建立ActivityThread執行個體
            ActivityThread thread = new ActivityThread();
            thread.attach(false);

            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }

            AsyncTask.init();

            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }

            <strong>Looper.loop();</strong>

            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
           

----------割割割-------------

在這裡其實我自己是有一個疑問的,主線程中loop()後會一直阻塞,那麼android就是不停的loop,實作消息傳遞的結果麼?還希望有人可以指導一下~~~麼麼哒(づ ̄ 3 ̄)づ

自問自答好了。。

http://stackoverflow.com/questions/6984263/android-looper-and-call-stack

everything that happens on the UI thread is through that loop

這樣看來。。确實android的顯示就是一個loop的死循環

繼續閱讀