Handler机制——Handler,Message,MessagQueue,Looper

请先参阅 Handler发送消息和延时Message实现原理。
为什么没有先讲Looper和MessageQueue是因为想从使用的角度来分析整个流程,对于开发者来说,第一个使用的其实是Handler。
前边我们分析过,Handler在sendMessage时,Message会有一个target指向当前的Handler,并且Message是一个单链表结构。我们来一起看下Message的源码。

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

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

*/ public final class Message implements Parcelable { /** * User-defined message code so that the recipient can identify * what this message is about. Each {@link Handler} has its own name-space * for message codes, so you do not need to worry about yours conflicting * with other handlers. */ public int what; /** * arg1 and arg2 are lower-cost alternatives to using * {@link #setData(Bundle) setData()} if you only need to store a * few integer values. */ public int arg1; /** * arg1 and arg2 are lower-cost alternatives to using * {@link #setData(Bundle) setData()} if you only need to store a * few integer values. */ public int arg2; /** * An arbitrary object to send to the recipient. When using * {@link Messenger} to send the message across processes this can only * be non-null if it contains a Parcelable of a framework class (not one * implemented by the application). For other data transfer use * {@link #setData}. * *

Note that Parcelable objects here are not supported prior to * the {@link android.os.Build.VERSION_CODES#FROYO} release. */ public Object obj; /** * Optional Messenger where replies to this message can be sent. The * semantics of exactly how this is used are up to the sender and * receiver. */ public Messenger replyTo; /** If set message is in use */ /*package*/ static final int FLAG_IN_USE = 1; /** Flags reserved for future use (All are reserved for now) */ /*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE; /** Flags to clear in the copyFrom method */ /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE; /*package*/ int flags; /*package*/ long when; /*package*/ Bundle data; /*package*/ Handler target; /*package*/ Runnable callback; // sometimes we store linked lists of these things /*package*/ Message next; private static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 10; }

可以看到Message有一个名为next,类型为Message的field,这个说明了Message本身是一个单链表的结构。what表示消息的类型,when的作用前边已经分析过了,obj我们一直在使用。另外需要额外注意的是名为target类型为Handler的field,还有名为callback类型为Runnable的field。现在只需要知道我们使用Handler发送的都是Message对象,Message对象身上有一个指向对应Handler的target属性。其它的例如obtainMessage和Messenger请看后续分解。
Handler发送的Message最终是通过MessageQueue的enqueueMessage来加入消息队列的。所以第一个问题是Handler里边的mQueue是怎么来的。让我们看下Handler的源码。

/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link 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. * *

Scheduling messages is accomplished with the * {@link #post}, {@link #postAtTime(Runnable, long)}, * {@link #postDelayed}, {@link #sendEmptyMessage}, * {@link #sendMessage}, {@link #sendMessageAtTime}, and * {@link #sendMessageDelayed} methods. The post versions allow * you to enqueue Runnable objects to be called by the message queue when * they are received; the sendMessage versions allow you to enqueue * a {@link Message} object containing a bundle of data that will be * processed by the Handler's {@link #handleMessage} method (requiring that * you implement a subclass of Handler). * *

When posting or sending to a Handler, you can either * allow the item to be processed as soon as the message queue is ready * to do so, or specify a delay before it gets processed or absolute time for * it to be processed. The latter two allow you to implement timeouts, * ticks, and other timing-based behavior. * *

When a * process is created for your application, its main thread is dedicated to * running a message queue that takes care of managing the top-level * application objects (activities, broadcast receivers, etc) and any windows * they create. You can create your own threads, and communicate back with * the main application thread through a Handler. This is done by calling * the same post or sendMessage methods as before, but from * your new thread. The given Runnable or Message will then be scheduled * in the Handler's message queue and processed when appropriate. */ public class Handler { /* * Set this flag to true to detect anonymous, local or member classes * that extend this Handler class and that are not static. These kind * of classes can potentially create leaks. */ private static final boolean FIND_POTENTIAL_LEAKS = false; private static final String TAG = "Handler"; /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. */ public interface Callback { public boolean handleMessage(Message msg); } /** * 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); } } /** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */ public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class 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 = null; } /** * Constructor associates this handler with the queue for the * current thread and takes a callback interface in which you can handle * messages. */ public Handler(Callback callback) { if (FIND_POTENTIAL_LEAKS) { final Class 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; } /** * Use the provided queue instead of the default one. */ public Handler(Looper looper) { mLooper = looper; mQueue = looper.mQueue; mCallback = null; } /** * Use the provided queue instead of the default one and take a callback * interface in which to handle messages. */ public Handler(Looper looper, Callback callback) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; } final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; IMessenger mMessenger; }

我们重点关注Handler的三个field,分别是mQueue,mLooper,mCallback。通过查看Handler的构造方法,我们可以清楚的看到,其实主要就是为这个三成员变量来赋值的。其中当没有looper的构造中,会把当前线程中的Looper赋值给mLooper,然后检查mLooper时可能会报我们最常见的关于Handler的错误"Can't create handler inside thread that has not called Looper.prepare()",完成mLooper赋值后,直接把mLooper的mQueue赋值给Handler的mQueue。
到此为止,我们已经明白了Message的产生和结构,然后Handler把Message通过MessageQueue 的enqueueMessage加入到消息队列。剩下的问题就是消息队列是如何把消息取出并执行的。
这就需要分析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
  * {@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.
  * 
  * 

Most interaction with a message loop is through the * {@link Handler} class. * *

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. * *

  *  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();
  *      }
  *  }
*/ public class Looper { private static final String TAG = "Looper"; // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal sThreadLocal = new ThreadLocal(); final MessageQueue mQueue; final Thread mThread; volatile boolean mRun; private Printer mLogging = null; private static Looper mMainLooper = null; // guarded by Looper.class /** 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() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new 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(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; } private synchronized static void setMainLooper(Looper looper) { mMainLooper = looper; } /** Returns the application's main looper, which lives in the main thread of the application. */ public synchronized static Looper getMainLooper() { return mMainLooper; } /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } 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(); while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } long wallStart = 0; long threadStart = 0; // 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); wallStart = SystemClock.currentTimeMicro(); threadStart = SystemClock.currentThreadTimeMicro(); } msg.target.dispatchMessage(msg); if (logging != null) { long wallTime = SystemClock.currentTimeMicro() - wallStart; long threadTime = SystemClock.currentThreadTimeMicro() - threadStart; logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); if (logging instanceof Profiler) { ((Profiler) logging).profile(msg, wallStart, wallTime, threadStart, threadTime); } } // 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.recycle(); } } } /** * 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(); } /** * Control logging of messages as they are processed by this Looper. If * enabled, a log message will be written to printer * at the beginning and ending of each message dispatch, identifying the * target Handler and message contents. * * @param printer A Printer object that will receive log messages, or * null to disable message logging. */ public void setMessageLogging(Printer printer) { mLogging = printer; } /** * 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 MessageQueue myQueue() { return myLooper().mQueue; } private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); } public void quit() { Message msg = Message.obtain(); // NOTE: By enqueueing directly into the message queue, the // message is left with a null target. This is how we know it is // a quit message. mQueue.enqueueMessage(msg, 0); } /** * Return the Thread associated with this Looper. */ public Thread getThread() { return mThread; } /** @hide */ public MessageQueue getQueue() { return mQueue; } }

Looper的重要的field分别是:
final MessageQueue mQueue;
final Thread mThread;
重要的方法是:
prepare和loop。
构造方法当然重要了。在唯一的构造方法中,new了一个MessageQueue赋值给mQueue,然后把当前线程赋值给mThread,完成对两个重要的field赋值。但是不要想当然,构造方法是private的,我们是无法直接调用的。答案就在另外一个重要的方法里边,prepare。
查看prepare的源码我们可以清楚的看到, sThreadLocal.set(new Looper());确实new了一个Looper对象存储到sThreadLocal中,关于ThreadLocal后边有机会了再详细分析,大家可以简单的理解为它是一个key相同,但value在各个线程不同的存储空间。这样我们就明白了,prepare不能调用多次,那大家有没有想过为什么呢。个人猜测是这样的,如果第二次调用prepare就会再次调用构造方法,然后mQueue的值就发生改变了,而我们知道,消息队列里边是有延时消息的,也就是还没有消费的消息,如果直接修改mQueue就可能造成消息的丢失。
查看loop的源码,我们可以看到,最重要的是一个while(true)的循环。里边通过MessageQueue的next方法取得一个Message,然后调用Message的target即对应Handler的dispatch方法来处理消息,然后直接recycle就结束了。很简单是吧,里边的核心是MessageQueue的next方法和Handler的dispatch方法。我们先来分析dispatch。

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

前边我们分析过,当我们使用handler的post方法时,我们post的Runnable对象被赋值到msg给callback,这就是第一种情况,调用的是handleCallback(msg)。

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

直接调用我们的Runnable对象的run方法,结束了这个消息的整个调用(所以我们post的那些代码是这个时候才执行的)。
当我们是使用Handler的send方法来发送Message时,首先检查mCallback是否为空,这个mCallback都是通过构造进行赋值的,这个就不再分析了。当mCallback为空时,调用的是我们最熟悉的handleMessage方法,就是我们平时需要重写的Handler的最重要的方法,在这也不再分析了。


    final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount == 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

后边的大部分代码都是处理刚开始的初始化情况。当开始正常运行后,重要的是前半段,取出一个mMessages,然后比较时间看看是否需要执行,如果需要执行,会让线程停止阻塞,然后把mMessages指向下一个,把已经取出的msg标记成使用中然后返回给Looper。如果还不需要执行,会算出一个时间差,然后下一个循环会调用nativePollOnce,猜测是一个阻塞等待的方法,跟enqueueMessage的nativeWake功能相反。
现在我们来总结一下,每个线程只会调用一次Looper.prepare(),也就决定了每个线程只会有一个Looper和一个MessageQueue,我们在不同的Activity中都可以new Handler,所以Handler可以是多个的。我们在子线程中new Handler会报错,但是在UI线程却没有问题,原因是因为UI线程已经调用过Looper.prepare()和Looper.loop()。我们可以查看代码是在ActivityThread的main方法中调用的。有人可能会有疑问loop之后线程就是阻塞式的,存在一个死循环,那我们的UI线程是怎么处理的呢,我们的Activity等是怎么切换的呢。答案很简单,所有的操作都是通过Message机制来实现的,只不过系统不需要new Handler,它的操作更直接,有兴趣的可以查看Instrumentation类的源码。

你可能感兴趣的:(Handler机制——Handler,Message,MessagQueue,Looper)