前言

Handler消息机制是Android中提供的一种线程间通信机制。说到线程间通信,懂Java的我们都知道可以通过synchronized、(wait/notify)实现线程间通信,但是这种机制会产生锁的竞争、线程的阻塞。为保持用户界面流畅UI线程不能被阻塞,耗时的任务又不能在UI线程操作,所以需要单独开一个工作线程,但是UI线程是非线程安全的,所以除UI线程外其他线程又不可执行UI操作,最后还是要回到UI线程更新UI,这就需要多线程之间的通信。可Java中线程间通信又都是阻塞式方法,所以传统的Java多线程通信方式在Android中并不适用。

为此Google开发人员就不得不设计一套UI线程与Worker线程通信的方法。既能实现多线程之间的通信,又能完美解决UI线程不能被阻塞的问题。Handler机制应用而生。

Handler消息机制符合生产者消费者模型,有以下几个角色组成。Looper、MessageQueue、Message、Handler,下面分别来介绍。

Handler整体工作流程

简要概述下Handler消息机制的整体运作流程,然后分别拆解每个流程的一些核心思想

Handler通过sendMessage方法将消息发送到MessageQueue中,Looper不断的循环从MessageQueue获取消息,然后拿到消息之后交给Handler处理。工作流程图如下:

Handler是什么java java handler机制的原理_UI

分析Looper的关键源码

Looper是线程隔离的。每个线程拥有独立的Looper对象,线程隔离的实现方式是通过ThreadLocal,接下来我们来看下Looper类的关键代码

Looper类暴露了一个静态方法prepare用来进行Looper对象的创建

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

可以看到,Looper对象是存储在ThreadLocal(线程私有变量)中的。第一次从ThreadLocal中获取,如果不存在,则创建Looper对象,然后存入到ThreadLocal中。注意对主线程的Looper对象,Looper类单独做了处理,暴露了两个静态方法用于创建主线程的Looper对象和获取主线程的Looper对象

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

然后在看下myLoop方法,该方法用来获取当前线程的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;
        // 死循环,不断的从消息队列获取消息
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            final Printer logging = me.mLogging;
            //记录消息分发前,利用这个可以统计消息处理的时间,检测卡顿测原因,BlockCanary就是实现自己的logging,来分析日志数据。
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // 将消息分发给Handler处理  msg.target就是该消息携带的Handler
            msg.target.dispatchMessage(msg);
            //记录消息分发后
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            // 消息回收
            msg.recycleUnchecked();
        }
    }

可以看出来loop是一个死循环,不断的从MessageQueue中读取消息。然后调用msg.target.dispatchMessage(msg);分发给Handler处理。消息处理完毕之后进行回收。当然还有一些额外获取消息队列、线程等方法,就不再一一介绍,看源码即可。

分析MessageQueue

MessageQueue 可以理解为一个消息队列,但是实际并不是采用队列这种数据结构,而且一个单链表
MessageQueue 主要用来存储消息和获取消息,是一个消息容器。接下来我们看下MessageQueue的关键代码。

Message next() {
        // Looper循环如果退出了,这里返回null,消息循环退出
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // idleHandler数量
        int nextPollTimeoutMillis = 0;// 睡眠时间
        // 死循环
        for (;;) {
            // 这里调用了C++层,实现阻塞,CPU休眠,nextPollTimeoutMillis为休眠时间
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                // 系统开机到现在的运行时间,相对时间
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // msg.target == null 只有一种可能,该消息是屏障消息
                if (msg != null && msg.target == null) {
                // 这里遇到屏障之后,会跳过所有的同步消息,直接获取异步消息,相对于提高了异步消息的一个优先级。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    // when代表消息的执行时刻
                    if (now < msg.when) {
                        // 还没到消息的执行时间,继续睡眠(msg.when - now)的时间
                        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;
                        // 标记消息在使用
                        msg.markInUse();
                        // 返回消息
                        return msg;
                    }
                } else {
                    // 消息队列没有消息,nextPollTimeoutMillis = -1 一直睡眠下去
                    nextPollTimeoutMillis = -1;
                }
                // 退出了
                if (mQuitting) {
                    dispose();
                    return null;
                }
                // 这里是处理IdleHandler,条件为,消息队列没有消息或者延迟消息还没到时间
                
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    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);
            }

            // 执行IdeHandler
            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(TAG, "IdleHandler threw exception", t);
                }
                // keep = false 执行一次就会移除
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // 重置pendingIdleHandlerCount = 0,所以IdleHandler只会执行一次
            pendingIdleHandlerCount = 0;

            // 执行到IdleHandler之后,这会可能有消息到来了,这里nextPollTimeoutMillis=0,直接开始运行,不会睡眠了
            nextPollTimeoutMillis = 0;
        }
    }

可以看出来next方法是一个死循环,不断地从MessageQueue中取出消息。从代码注释看出有几个关键词: 同步屏障、异步消息、IdleHandler、阻塞睡眠,我们稍后解释

继续分析下enqueueMessage方法

boolean enqueueMessage(Message msg, long when) {

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, 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) {
                // New head, wake up the event queue if blocked.
                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; // invariant: p == prev.next
                prev.next = msg;
            }

            // 唤醒
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
分析Handler

先看下Handler的构造函数

public Handler() {
        this(null, false);
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Looper looper) {
        this(looper, null, false);
    }

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

    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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

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

构造函数重载的比较多,稍微看下,这里主要分析下dispatchMessage方法,上一节说到
Looper循环loop方法取到消息之后会调用msg.target.dispatchMessage方法,这个target就是消息携带的Handler对象,这里分析下Handler的dispatchMessage方法:

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

分析下方法回调的优先级,第一优先级,如果消息设置callback,则执行handleCallback

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

比如我们平时handler.post(Runnable runnable) ,这时候内部创建了一个Message,并且message.callback=runnable;所以消息处理的时候会执行run方法。第二优先级,如果构建Handler的时候传入了Callback,则会执行这个callback的handleMessage方法

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

此方法带有返回值,如果返回了false,则证明此消息没有被处理掉,那么继续回调handleMessage方法。

public void handleMessage(Message msg) {
    }

如果返回true,则已经处理完毕,return结束。这样一次完整的消息分发就结束了!

分析Message
public final class Message implements Parcelable {
   
    public int what;// 消息标识

    public int arg1;
    
    public int arg2;

    public Object obj;

    public Messenger replyTo;
    
    static final int FLAG_ASYNCHRONOUS = 1 << 1;// 标记是否是异步消息

    int flags;

    long when;// 消息执行时间

    Bundle data;

    Handler target; // 携带的handler对象

    Runnable callback;// 回调runnable

    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 = 50;// 消息池默认大小50

}

Message就是一个数据讯息载体,这里Message的设计采用了享元设计模式,减少了对象的频繁创建,实现了对象复用,消息池默认大小50

关键词解耦
  • 同步消息屏障

同步屏障可以通过MessageQueue#postSyncBarrier函数来设置,代码如下

public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
}
    
private int postSyncBarrier(long when) {
        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) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

可以看到,就是将消息队列插入了一条消息,根据消息的when时间进行排序放到对应的位置,特殊之处在于该条消息的target == null。所以这一步就对接上我们分析MessageQueue#next方法中的注释了。所以,从代码层面上来讲,同步屏障就是一个Message,一个target字段为空的Message。

  • 异步消息

通常我们使用Handler发消息时,这些消息都是同步消息,如果我们想发送异步消息,那么在创建Handler时使用以下构造函数中的其中一种(async传true)

public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);

然后通过该Handler发送的所有消息都会变成异步消息,也可通过Message#setAsynchronous()设置为异步消息。

异步消息是与同步屏障一起连用的。分析MessageQueue#next()取消息时,如果检查到同步屏障,那么会跳过屏障之后的同步消息,直接获取异步消息,所以同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。

  • 同步屏障的使用场景

Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //设置同步障碍,确保mTraversalRunnable优先被执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //内部通过Handler发送了一个异步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

mTraversalRunnable调用了performTraversals执行measure、layout、draw

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障

问题思考?
  • Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
    通过分析MessageQueue#next得知,消息队列在没有消息时,或者延时消息没到时间,会调用nativePollOnce()进行睡眠,这是native层的方法,通过native层实现的cpu的休眠,所以并不是无限循环的执行,一直占用着资源。有了睡眠自然有唤醒操作,在分析MessageQueue#enqueueMessage方法,我们得知,在有同步消息到来,或者新消息执行时间小于队列头部的消息执行时间,这里会调用nativeWake执行唤醒操作。
  • Handler中延迟消息的实现?
    通过上面的分析,其实延时消息的实现我们已经知道了,首先延时消息插入消息队列,会按照when时间进行排序,放到指定的位置,如果时间没到,那么会调用nativePollOnce()进行阻塞休眠,时间到了被执行。当然在休眠期间会有唤醒操作,MessageQueue#enqueueMessage,每次唤醒都会比对下时间,时间没到,还是会继续睡眠
  • IdeHandler怎么理解
    通过以上的分析,其实IdleHandler是一种闲时任务处理机制,在MessageQueue#next方法中得知,在消息队列没有消息时,或者延时消息还没到时间,就会检查是否存在IdleHandler,如果有则会遍历调用所有的IdleHandler调用其queueIdle方法进行任务执行。