Android異步消息處理機制

關于Android中的異步消息處理機制,平時在項目中應該算是用的很多了。最近看了一些這方面的源碼,記錄一下。

首先,來看一下平時是怎么用的吧。最常見的使用場景,可能就是在子線程中處理一些耗時操作,然后更新UI了。那么,這是怎么做的呢?

在子線程中處理耗時操作,然后新建了一個Message(這個Message里面會包含很多內容,一會兒再細說),通過一個Handler將Message發送出去。

        new Thread(new Runnable() {
            @Override
            public void run() {
                // 執行耗時操作

                // 發送消息
                Message message = Message.obtain();
                message.what = 1;
                message.arg1 = 2;
                handler.sendMessage(message);
            }
        }).start();

在主線程中,我們有一個Handler,并且重寫了handleMessage方法,用來接收并處理消息。

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                // 更新UI

            }
        }
    };

其實我們常見的就是Handler和Message這兩個東西,那么就先來看一下他們到底是什么吧。

Handler

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.

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的定義,簡單來說,Handler就是用來發送和處理Message的,而且很重要的一點,每個Handler的實例都關聯了一個線程和這個線程的MessageQueue

Message

Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.

什么是Message?Message就是一個包含了描述和任意數據的對象,可以被發送給Handler進行處理。

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

官方在這里明確表示,雖然Message的構造函數是公有的,但是最好的方式是通過Message.obtain()或者Handler.obtainMessage()之類的方法來創建Message,因為這樣會從一個全局的池中來復用Message,而不是每次都新建一個。

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

MessageQueue

Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.

前面說了每個Handler的實例都關聯了一個MessageQueue,那么MessageQueue是什么?其實從名字就能很容易的看出,MessageQueue就是存儲Message的一個隊列,先進先出。那么MessageQueue是在哪呢?接下來看另一個很重要的東西Looper。

Looper

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就是一個無限循環的任務,他不斷的從MessageQueue中嘗試取出Message,如果沒有取到就繼續嘗試,如果取到了就交給Hander去處理,直到這個循環被停止。

好了,說了這么多概念,讓我們來看看源碼。還是從Handler開始,畢竟是直接和我們打交道的。

無論使用哪種方式創建Handler,最后其實都離不開這兩個構造函數。

1 使用當前線程默認的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;
    }

2 使用指定的Looper而不是默認的

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

區別就在于是使用當前線程的默認Looper還是使用指定的Looper了。這里我們可以看到,如果使用的是默認Looper,會調用Looper.myLooper()方法。
讓我們來看看Handler中有哪些常用的方法,基本可以分為兩大類post(Runnable r)sendMessage(Message msg),而他們的區別在于post類的方法會調用下面這個方法,指定Message的callback。

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

然后返回一個Message,最終基本上都會調用到這個方法

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

其中MessageQueue queue = mQueue;指定了Handler對應的MessageQueue,然后調用入隊列的方法

    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是將Message和Handler綁定在了一起,而queue.enqueueMessage(msg, uptimeMillis)就是將Message入到隊列MessageQueue中去。

Message就是我們需要發送和處理的消息,其中大概包含了這么幾個我們需要用到的東西。

  • public int what
    我們自定義的一個類似編號的東西,可以用來與其他Message進行區分
  • public int arg1,arg2
    可以存一些輕量級的數據
  • public Object obj
    可以存對象
  • Bundle data
    用來存儲復雜的數據
  • Handler target
    與Message關聯的Handler,也就是負責處理消息的Handler
  • Runnable callback
    可以指定運行在某個Runnable上

再來看一下Looper。

  • 構造方法
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper的構造方法中只做了兩件事:
1、生成了一個MessageQueue,這里也就明白了前面說的保存Message的這個隊列MessageQueue在哪里了,沒錯,就是在Looper里。
2、得到當前所在的線程。
構造方法做的這兩件事很重要,說明了每一個Looper中都包含了一個MessageQueue,并且關聯了一個特定的線程,這也是異步消息處理的關鍵。

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

prepare方法會檢查是否已經有Looper存在,如果存在會拋出異常,因為一個線程中只能有一個Looper存在,如果不存在則創建一個新的Looper,存在一個ThreadLocal里。

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

我看到網上很多文章在講Handler機制時并沒有過多的提到ThreadLocal,但是其實我覺得這也是很重要的一部分。到底什么是ThreadLocal?簡單來說就是將對象在不同線程中分別保存了不同的副本,在某一個線程中改變了對象的值時,并不會影響其他線程中的副本。所以這里用來保存Looper,不同線程中的MessageQueue將會互不影響。
如果我們在子線程中創建Handler之前不調用prepare方法,將會拋出異常,但是為什么我們在主線程中不需要調用prepare方法呢?那是因為系統會自動調用prepareMainLooper方法為我們創建主線程的Looper。

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

而前面在Handler的構造函數中的myLooper方法,只是從ThreadLocal中取出Looper而已。

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

looper方法就是一個無限循環的任務,不斷的從MessageQueue中取出Message進行處理。其中final MessageQueue queue = me.mQueue;就是從當前Looper中取出了MessageQueue,而msg.target.dispatchMessage(msg);就是真正處理消息的地方,其中msg.target就是與Message綁定在一起的那個Handler。

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

callback是什么?callback就是通過post類的方法指定的Runnable,如果callback不為空就執行

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

否則就執行handleMessage(msg);

看到這里不難發現,就像官方的定義那樣(廢話,當然一樣。。。),每個Handler都對應了一個Looper和一個MessageQueue,關聯了一個Thread,所以才能實現異步消息處理。比如我們在主線程上創建了一個Handler,就創建了一個Looper和MessageQueue,關聯的就是主線程,然后在子線程中發出消息,這個消息就會存儲在主線程上的MessageQueue里,并且由Handler進行處理。這樣就完成了在子線程中發送消息,在主線程中處理的過程,當然這只是其中的一種應用場景。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容