摘要:本文按照Handler的使用流程逐步分析AndroidFramwork层中Handler的运行机制,涉及到的类包括Handler.java、Looper.java、Message.java、MessageQueue.java、HandlerThread.java,这些类均在android.os包里。

1. Handler的使用

    mHandler = new Handler(){
    	@Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    Message msg = mHandler.obtainMessage(what);
    mHandler.sendMessage(msg);

从上述代码可知,我们使用Handler的流程:

    创建Handler -> 获取消息对象 -> 发送消息 -> 处理消息

2. Handler的创建

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

构建函数中比较重要的是looper(Looper)对象,mQueue(MessageQueue)对象是与looper相关联的,mCallbackmAsynchronous后续会讲解。

3. Looper的来历

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

构建函数中设置当前线程对象到mThread变量中,并创建了一个MessageQueue对象,其创建过程我们稍后分析。心细的读者可能已经发现这是个私有构造函数,那么肯定是在类内部进行调用的,搜索发现其调用函数为:

    public static void prepare() {
        prepare(true);
    }

    // 目前quitAllowed应该仅在主线程中被设置成false,自定义线程中均为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));
    }

Looper#prepare方法会创建Looper对象,并且某一线程仅能创建一个Looper对象,然后将创建的对象设置为线程安全变量。那么这个函数在哪里调用咧?既然跟线程相关,那么大胆猜测是在线程中调用的,而Android中最主要的线程是主线程,我们一开始的“Handler的基本使用”中并没有创建Looper,却可以使用Handler,那么Android主线程中必然在生成的时候就新建了一个Looper,查看Looper源码时会发现的确含有一个特殊的Looper(即:sMainLooper),这个特殊Looper对象通过调用Looper#prepareMainLooper()创建的,我们可以去ActivityThread中去看是如何实现的:

    public static void main(String[] args) {
        //···
        Looper.prepareMainLooper();
        //···
        Looper.loop();
        //···
    }

上述代码已去掉无关代码,至此我们就了解了Looper的来历,但之后调用的Looper#loop()是个什么鬼?这个主要是进入一个不断从MessageQueue中获取消息进行处理的无限循环,在分析这个循环之前我们要先来分析下MessageQueue是个什么鬼。

4. MessageQueue是什么鬼

    mQueue = new MessageQueue(quitAllowed);

上面创建Looper的时新建了一个MessageQueue对象,看下它的构造函数:

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

    private native static long nativeInit();

WFK,你就给我看这个!!!C++学渣表示强烈抗议。对于Native感兴趣的小伙伴可以自行查看源码,我跟着老罗的文章<Android应用程序消息处理机制(Looper、Handler)分析>稍微感受了下源码的逻辑,nativeInit就是在native层创建了一个NativeMessageQueue及一个Looper对象,而native层的Looper对象利用管道机制来监控文件,从而可以利用epoll机制实现MessageQueue的等待和唤醒,这个在MessageQueue#next()会进一步分析。至此,MessageQueue对象就被创建出来了(由于MessageQueue的构建函数仅有包访问权限,因此正常情况下我们无需关心MessageQueue的创建),然后我们来看下Looper#loop()到底在做什么。

5. Looper#loop()到底正在做什么

    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        //···省略无关代码
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //···省略无关代码
            msg.target.dispatchMessage(msg);
            //···省略无关代码
            msg.recycleUnchecked();
        }
    }

Looper#loop()方法只有当Looper被创建出来之后方可调用,主要包括一个无限循环:从MessageQueue中获取消息进行处理,然后回收处理完的消息。我们先来看MessageQueue#next():

	Message next() {
        // 在Looper#loop()中我们知道返回空消息会退出loop()中的无限循环
        // 当调用MessageQueue#quit(boolean)时会调用nativeDestory()销毁MessageQueue,将ptr置为0
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // 仅在第一次调用时为-1
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            /**
            * 这个调用跟上面提到native层中Looper的epoll机制相关,用于等待可处理的消息
            * nextPollTimeoutMillis < 0 : 进入无限空闲等待,直到有新消息唤醒
            * nextPollTimeoutMillis = 0 : 不等待
            * nextPollTimeoutMillis > 0 : 进入空闲等待,直到有新消息唤醒或者nextPollTimeoutMillis超时
            **/
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // 注:MessageQueue管理的消息是一个消息链表,后续Message中会详细分析
                if (msg != null && msg.target == null) {
                    /**
                    * msg.target为空是一类特殊消息(栅栏消息),用于阻塞所有同步消息,但是对异步消息没有影响,
                    * 后续会详细分析。在这个前提下,当头部是特殊消息时需要往后找是否有异步消息
                    */
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                	// 找到消息
                    if (now < msg.when) {
                        // 消息的触发时间在当前时间之后,于是计算出需要等待的时间,准备进入有限空闲等待
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                    	// 找到可处理的消息,更新消息链表数据,返回可处理消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // 没找到消息,准备进入无限空闲等待
                    nextPollTimeoutMillis = -1;
                }

                // 没有可处理的消息,并且消息队列已经退出,则返回空消息让loop退出
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // 当mMessages为空或者mMessages的处理时间在当前时间之后(注意栅栏消息的特殊情况)时,
                // 并且pendingIdleHandlerCount没有在此处初始化过,
                // 则设置pendingIdleHandlerCount为IdleHandler的数量,IdleHandler后续详细说明。
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // 无空闲处理器,阻塞队列,进入空闲等待
                    mBlocked = true;
                    continue;
                }

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

            // 调用空闲处理器逻辑,此处代码仅调用一次
            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);
                    }
                }
            }

            // 设置为0保证空闲处理器代码仅调用一次
            pendingIdleHandlerCount = 0;

            // 在处理空闲处理器的时候可能已经有可处理的消息,因此无需等待
            nextPollTimeoutMillis = 0;
        }
    }

由于这个函数是消息获取的关键,因此是无删减版。而相关说明直接注释在代码中,主要目的在于建议小伙伴们看源码。我相信读完源码应该对获取消息的机制有了比较完整的了解,当loopMessageQueue中获取到消息便可以进行消息处理并在处理后回收该消息,具体等我们分析完消息的发送之后再来看。

6. Handler#sendMessage中的消息怎么获取到

    public final Message obtainMessage(){
        return Message.obtain(this);
    }

直接调用Message#obtain(Handler):

    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

直接调用Message#obtain() - -!:

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

这里会先从回收池中取消息,如果没有就新创建一条消息;回收池是一个消息链表,sPoolSync是同步符号,sPool是链表头,sPoolSize是回收池中可用消息数,最大限制为MAX_POOL_SIZE(默认为50)。这些都没有太大可分析性,我们来看看Message的结构:

	public int what;	// 消息码,带Handler命名空间,因此不同Handler中相同消息码不冲突
	public int arg1;	// 整数数据
	public int arg2;	// 整数数据
	public Object obj;	// 任意对象,当利用Messenger进行跨进程传递时需要继承自Parcelable

	public Messenger replyTo;	// Messenger对象实现跨进程消息传递
	public int sendingUid = -1;	// 跨进程是标记消息来源的Uid

	/**
	* flags可设置消息是否在使用以及是否异步
	* FLAG_IN_USE = 1 << 0,该标记只有在创建或obtain时才会清除,此时方可修改消息的相关数据及进行发送
	* FLAG_ASYNCHRONOUS = 1 << 1,标记该消息为异步消息,不受栅栏消息的影响
	**/
	/*package*/ int flags;

        /*package*/ long when;	// 消息执行时间,采用SystemClock#uptimeMillis()时间base
        /*package*/ Bundle data;	// 消息的数据
        /*package*/ Handler target;	// 消息对应的Handler
        /*package*/ Runnable callback; // 消息对应的回调,具体参看下文中消息处理一节

        /*package*/ Message next; // 形成消息链表,以在MessageQueue以及消息回收池中使用

至此,消息的来源以及消息的结构分析完毕,其中flagsMessag自己管理,datagetDatapeekData以及setData进行管理,targetcallbackHandler中相关获取或发送消息的接口管理。获取到消息之后,便可以调用Handler的消息发送接口进行发送,那是如何进入MessageQueue的咧?

7. Handler#sendMessage中的消息怎么放入消息队列

Handler中的消息发送接口除了Handler#sendMessageAtFrontOfQueue(Message)均会调用Handler#sendMessageAtTime(Message),而这两个接口最终调用了Handler#enqueueMessage(MessageQueue, Message, long):

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

根据HandlermAsynchronous属性设置消息的异步属性,最后调用MessageQueue#enqueueMessage(Message, long):

	// 插入成功返回true,否则返回false
	boolean enqueueMessage(Message msg, long when) {
		// Handler中不允许发送target为空的消息,空消息为特殊消息(栅栏消息)
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // 不允许发送状态为使用中的消息
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
            	// 不允许发送消息给已退出的消息队列
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // 消息触发时间最早,直接插在链表头部,如果当前队列阻塞则唤醒消息队列的等待,见MessageQueue#next
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // 将消息插入到链表中间,如果链表头是栅栏消息并且该消息是触发时间最早的异步消息则需要进行唤醒
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p;
                prev.next = msg;
            }

            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

建议查看源码,相关说明已注释在代码中。把新消息放入队列,并在必要的时候唤醒消息队列进行处理,从而就回到上述MessageQueue#next的逻辑中,然后在有可处理消息的时候将消息发送到Looper#loop中进行处理及回收。

8. Message的处理及回收

当消息被返回到loop中时,调用:

	msg.target.dispatchMessage(msg);
	msg.recycleUnchecked();

target就是Handler,于是调用:

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

上述代码表明:当消息本身回调不为空时则由消息本身回调处理该消息;当HandlermCallback不为空时则由HandlermCallback处理消息;否则则由Handler中的钩子handleMessage进行处理。消息处理完了之后需要将消息回收:

	void recycleUnchecked() {
        // 标记为使用中,清除所有数据
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        // 放入消息回收池
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

至此,整个Handler的运行机制(创建Handler -> 获取消息对象 -> 发送消息 -> 处理消息)就分析完了。

9. 一些其他补充

9.1 栅栏消息

栅栏消息target为空的特殊消息,用于延迟MessageQueue中所有指定时间之后的同步消息,异步消息则仍可执行。发送和移除栅栏消息必须成对出现,否则可能导致MessageQueue被挂起。 其发送移除接口在Looper中:

    public int postSyncBarrier() {
        return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
    }

    public void removeSyncBarrier(int token) {
        mQueue.removeSyncBarrier(token);
    }

调用了MessageQueue#enqueueSyncBarrier(long):

	int enqueueSyncBarrier(long when) {
        // 创建一个target为空的特殊消息,并根据when插入MessageQueue中合适的位置
        // 无需唤醒因为栅栏消息的目的在于阻塞消息的执行
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) {
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

必须成对出现的MessageQueue#removeSyncBarrier(token),其中tokenenqueueSyncBarrier返回:

	void removeSyncBarrier(int token) {
        // 移除token对应的栅栏消息,并在必要的时候进行唤醒
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

有没有觉得,so easy - -!

9.2 空闲处理器(IdleHandler)

IdleHandler定义在MessageQueue中:

    public static interface IdleHandler {
        // 返回true表示保持在MessageQueue的mIdleHandlers中
        boolean queueIdle();
    }

具体调用时机见MessageQueue#next中的分析。

9.3 Handler的相关接口介绍

获取消息(带不同参数):

    final Message obtainMessage()
    final Message obtainMessage(int what)
    final Message obtainMessage(int what, int arg1, int arg2)
    final Message obtainMessage(int what, Object obj)
    final Message obtainMessage(int what, int arg1, int arg2, Object obj)

发送消息:

    final boolean sendEmptyMessage(int what)
    final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
    final boolean sendEmptyMessageDelayed(int what, long delayMillis)
    final boolean sendMessage(Message msg)
    final boolean sendMessageAtFrontOfQueue(Message msg)
    final boolean sendMessageDelayed(Message msg, long delayMillis)
    boolean	sendMessageAtTime(Message msg, long uptimeMillis)

发送带Callback的消息:

    post(Runnable r)
    final boolean postAtFrontOfQueue(Runnable r)
    final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    final boolean postAtTime(Runnable r, long uptimeMillis)
    final boolean postDelayed(Runnable r, long delayMillis)

移除消息:

    final void removeCallbacks(Runnable r)
    final void removeCallbacks(Runnable r, Object token)
    final void removeCallbacksAndMessages(Object token)
    final void removeMessages(int what)
    final void removeMessages(int what, Object object)

9.4 HandlerThread类

HandlerThread类是Handler的一个辅助类,当调用HandlerThread#start()之后,会创建一个带LooperThread

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

这样,我们使用非主线程Handler的时候便比较简单了:

    HandlerThread t = new HandlerThread(TAG);
    t.start();
    mHandler = new Handler(t.getLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
             // TODO: 处理消息
        }
    });	

10. 参考文献

  1. Android应用程序消息处理机制(Looper、Handler)分析 - - 罗升阳