Android-源碼解析Handler&Looper

Handler是可以用來發送消息,或者一個Runable;消息/Runable可以在指定的線程(由Handler中的Looper決定)中接受/執行;
MessageQueue:消息隊列,用來存放消息的
Looper:用來循環遍歷消息隊列中的消息,並負責消息的分發

我們看看使用Handler常用示例代碼(如下


public class TestActivity extends Activity {

    private Handler mHandler = new Handler(){

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case caseA:
                    doSomeThingA();
                    break;
                case caseB;
                    doSomeThingB();
                    break;
                default:
                    break;
            }
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sendMessage();
    }

    private void sendMessage() {
        Message msg = Message.obtain();
        msg.what = caseA;
        msg.obj = data;
        mHandler.sendMessage(msg);
    }
}

我們可以看到mHandler變量作爲成員變量,覆寫了handleMessage方法,且是通過無參構造器創建出來的;
在onCreate方法調用了sendMessage方法,這樣消息最終被傳送到handlerMessage中;下面我們結合Handler部分源碼看看消息是如何一步一步到達Handler的handleMessage方法裏的。

TestActivity類中的sendMessage方法中發送消息後,調用了mHandler.sendMessage(msg),也就是如下代碼,

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

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;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
}

我們從上述源碼可以看到,handler發送的消息最終是把消息放到消息隊列中,之後就沒做任何事情了,那麼問題來了

  1. 那麼消息隊列是在什麼時候創建?

    其實每個Looper都包含一個消息隊列成員,而消息隊列也是在Looper構造器時被創建;

    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
    }
  2. 最終消息是如何分發到handlerMessage中?

我們看到Handler類中包含了Looper 和 消息隊列成員變量,它們都是在構造器中被賦值(見下面Handler類部分源碼)

 /**
     * Default constructor associates this handler with the {@link Looper} for the
     * current thread.
     *
     * If this thread does not have a looper, this handler won't be able to receive messages
     * so an exception is thrown.
     */
    public Handler() {
        this(null, false);
    }
/**
     * 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());
            }
        }

        //獲取當前線程的Looper對象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

        //從Looper對象中獲取消息隊列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

因爲示例代碼中Handler對象是在UI線程中創建,而系統在初始化時會自動調用Looper.prepareMainLooper方法,該方法幫我們創建好了主線程的Looper對象(所以在UI線程中我們不需要主動調用Looper.prepareMainLooper去創建Looper對象);創建Looper對象後會啓用loop方法,它會不斷循環讀取消息隊列的消息。

我們看下Looper源碼:

package android.os;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
import android.util.Printer;

/**
  * 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
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */
public final class Looper {
    /*
     * API Implementation Note:
     *
     * This class contains the code required to set up and manage an event loop
     * based on MessageQueue.  APIs that affect the state of the queue should be
     * defined on MessageQueue or Handler rather than on Looper itself.  For example,
     * idle handlers and sync barriers are defined on the queue whereas preparing the
     * thread, looping, and quitting are defined on the looper.
     */

    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

    private Printer mLogging;

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

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

    /**
     * Returns the application's main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

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

            //target其實就是Handler,此處就是將消息傳遞到Handler中dispatchMessage方法中
            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();
        }
    }

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

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

我們可以看到上述代碼中loop方法,其內部就是不斷從消息隊列中取消息,最終調用msg.target.dispatchMessage(msg);這裏的target其實就是handler對象,也就是Handler的dispatchMessage方法;那麼dispatchMessage內部是怎麼實現?(看下面源碼)

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
        //如果Message中的Runnable不空,直接調用Message的Runable.run方法
            handleCallback(msg);
        } else {
        //如果Handler中構造器傳入了CallBack不爲空
            if (mCallback != null) {
            //如果CallBack不爲空且handleMessage返回爲true,直接返回(不執行handleMessage方法了)
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    private static void handleCallback(Message message) {
        message.callback.run();
    }

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

我們的示例代碼中發送的message的callback沒有賦值所以爲null,且我們的mHandler是無參構造的所以mCallback其實也是爲空,所以最終執行到Handler的handleMessage方法;整個大體流程就是這樣;

我們可以看到其實dispatchMessage方法運行在哪個線程其實是由Looper.loop決定的,至此整個流程逐漸清晰明朗起來了

梳理下流程:

Created with Raphaël 2.1.0HandlerHandlerMessageQueueMessageQueueLooperLooper(1)sendMessage (調用在A線程)最終是將消息添加到消息隊列中(2) Looper不斷從消息隊列中取消息(3)dispatchMessage(運行在loop調用的線程)(4)處理消息

通過Looper&Handler來達到線程間通訊

上面示例代碼演示消息最終在UI線程被消費處理掉,如果要將一個消息/Runable傳遞給一個線程我們該如何做呢?我麼可以這樣做

class LooperThread extends Thread {

        private Handler mHandler;

        @Override
        public void run() {
            super.run();

            //爲當前線程創建一個Looper對象
            Looper.prepare();

            //handler會獲取當前線程綁定的Looper對象
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };

            //循環分發消息隊列中的消息
            Looper.loop();
        }
    }

首先定義一個LooperThread,我們覆寫run方法,並對該線程綁定一個Looper對象和一個Handler對象,這樣在外部我們可以通過Handler對象將消息/Runable交給Handler的handlerMessage處理了;這樣就達到了其他線程消息交給此線程處理目的,這其實就實現了線程間通訊了。

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