Handler消息机制详解(一)

281 阅读7分钟

Handler是android系统中最重要的构成部分,它是消息机制的基础,同时也是整个系统运作的动力源,本篇将通过源码来分析Handler的消息机制,以便了解Android底层是如何实现消息机制。

消息机制简述

消息机制是一种事件机制,消息循环通过消息作为载体可以告知应用系统或者应用发生了什么事,应用根据消息内容做出反应,即消息处理。典型的在win32系统中基于消息循环的消息机制如下,通过不断的在UI线程读取消息内容并处理的过程,就是应用执行的流程:

while(GetMessage(&msg, NULL, 0, 0))
{
    if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg)){ 
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

Android的消息机制类似于win32的消息机制,它内部通过Looper来进行消息的循环,同样的,它也有自己的消息队列,通过循环读取消息队列的消息维持应用的运行。

Handler、Looper与MessageQueue、Message

要理解Android的消息机制,需要理解Handler、Looper与 MessageQueue以及它们之间的关系。这里我们分别做说明:

  • Handller handler负责消息的发送和处理,如果将消息流程看做一个链式处理流程,它就处于前端和末端。关于handler的用处,官方的说法是:

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主要有两方面的作用:

  1. 投递消息或者runnable在未来某个时间点上执行
  2. 可以添加一个可以被执行的动作在不同的线程中,也就是可以实现线程切换
  • Looper Looper即消息循环,类似于上面的while循环,它不断的从消息队列中取消息,并通过handler进行处理,它会和一个线程关联起来。

  • MessageQueue 即消息队列,它用来存储handler投递的消息。

  • Message 消息实体,我们发送的消息会被封装成Message对象然后添加到消息队列中

消息的投递

handler的构造

我们知道了消息是通过Handler来进行投递的,那我们看看handler的构造方法,handler有多个构造方法,这里看我们平时用的那个,其他的读者自己分析

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

public Handler(Callback callback, boolean async) {
    ……
    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;
}

在handler的构造中,最主要的是取到线程关联的Looper,因为消息循环是在线程中不断处理消息事件的,而线程只能有一个消息循环,多了就没必要了。所以消息循环和线程是绑在一起的。这里我们将线程的Looper保存在Handler的mLooper中,同时Looper和一个MessageQueue关联,这里将其消息队列保存在mQueue中,我们通过Handler发送的消息就是添加到该队列中的。

Looper的构造

这里为什么直接可以通过Looper.myLooper获取到Looper对象呢?Handler的创建可以分为UI线程和非UI线程两种,这里我们关注在UI线程的创建,非UI线程的在分析HandlerThread时我们再说,由于UI线程在创建时调用了Looper的prepareMainLooper方法创建了Loope对象,所以这里能通过myLooper取到这个创建的Looper对象。

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

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

    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创建的Looper对象被保存在一个ThreadLocal对象中, ThreadLocal即线程本地变量,这里它会为每个线程保存一个Looper对象和线程关联。

MessageQueue的构造

在创建Looper时,会去创建Looper关联的消息队列MessageQueue

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

这里的quitAllowed表示是否允许退出消息循环,在UI线程这个为false,表示不允许退出。

消息结构

public final class Message implements Parcelable {
    public int what;
    public int arg1; 
    public int arg2;
    public Object obj;
    public Messenger replyTo;
    public int sendingUid = -1;
    long when;
    Handler target;
    Runnable callback;
    Message next;
    ……
}

从Message的结构中可以看到它支持由what指定的message和Runnable ,消息的参数通过arg1和arg2或者obj附带。target指定了处理消息的Handler。

Handler的投递

接下来我们看看消息的投递方法


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

public final boolean post(Runnable r)
{
    return  sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
……

handler主要用来做消息的投递,它有众多的投递方法,大体上分为两类,一类通过sendxx来发送message,另一类通过post来发送runnable,不过最终都他们都会封装成Message对象然后通过sendMessageAtTime来将其添加到消息队列中。

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

从函数sendMessageAtTime名字可以看出它将消息投递在未来某一个时间点上,当当前时间到达该时间点后触发该消息执行。这里添加消息是通过enqueueMessage来完成的。enqueueMessage中首先指定了消息的target为this,也就是当前Handler,然后通过MessageQueue的enqueueMessage添加消息到消息队列中。

MessageQueue消息的投递

通过MessageQueue的enqueueMessage将消息对象Message投递到其队列中。

boolean enqueueMessage(Message msg, long when) {
    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(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 {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            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;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

enqueueMessage的逻辑很简单,分两个情况:

  1. 添加的Message作为队列的头,这个时候根据需要唤醒等待消息事件的线程
  2. 添加的Message不作为队列头,Message会插入到MessagQueue队列中,这个时候需要根据消息的when即消息的投递到的时间点来插入到具体位置,这也是大多数的情况。从这里也可以看出消息队列是以when进行排序的,靠前的时间点处于队列的前面相比靠后的要先被取出来。

消息循环

当消息队列有消息时,消息循环就需要不停的进行处理,这个工作是Looper的loop方法来做的。同样在在UI线程创建时需要调用这个方法启动消息循环

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;
        }
       
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ……
    }
}

这里先判断Looper是否调用了prepare,前面我们知道prepare时才会创建Looper对象和消息队列,如果没有创建当然就不能启动消息循环,否则就可以拿到消息队列,进入for循环 不断的通过MessageQueue的next方法取出消息对象msg,然后通过msg的target即Handler调用dispatchMessage方法进行处理。这么看来Looper的逻辑是非常简单的。需要注意的是MessageQueue的next方法是可能阻塞的,因为当前队列可能还没message或者message指定的时间点还未到,这时线程就是阻塞的。接下来我们就看看next的具体实现

Message next() {
    
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            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);
        }

        // 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(TAG, "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;
    }
}

MessageQueue的next方法会返回一个队列中一个Message实体,也可能返回null。next的处理也是通过一个for循环处理的,因为next是可能阻塞的,它是通过nativePollOnce来实现的,它有两个参数ptr和nextPollTimeoutMillis,ptr代表了native层的MessageQueue,而nextPollTimeoutMillis代表了阻塞的时长,有以下几种可能:

  1. nextPollTimeoutMillis = -1 ,一直阻塞不超时
  2. nextPollTimeoutMillis = 0 ,不会阻塞,立即返回
  3. nextPollTimeoutMillis >0 ,最长阻塞nextPollTimeoutMillis毫秒,如果期间被唤醒后会立即返回。

循环之前nextPollTimeoutMillis为0 表示立即返回,然后通过队列中的头Message,来判断是否需要继续阻塞,满足now<msg.when表示还未到触发的时间点。否则从队列中取出该msg并返回给loop,并置mBlocked = false表示没有阻塞。

在取消息前,我们看到如下代码:

if (msg != null && msg.target == null) {
    // Stalled by a barrier.  Find the next asynchronous message in the queue.
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous());
}

这里的msg.target为null,它并不是一个普通的Message,而是一个Message屏障,这个屏障的作用就是使它之后的所有同步消息不会被执行。

我们看看这个屏障是如何添加的

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

private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    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;
    }
}

那么为什么Android消息机制会允许这个屏障消息的存在?或者说它有哪些使用的场景?事实上屏障消息的存在说明有某些重要的消息将要执行而不能受其他同步消息的影响。在framework中我们发现了postSyncBarrier被用在了ViewRootImpl的scheduleTraversals中,这个方法被用来启动UI绘制流程,UI绘制的过程会通过UI线程的Handler发送来自SurfaceFlinger的Vsync信号,Vsync信号是用来同步UI界面更新绘制的。总之,这个地方了解即可。

消息的投递

消息的投递过程是在Looper中开始的,通过msg.target的dispatchMessage方法来进行,我们知道msg.target就是Handler.


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

dispatchMessage的过程先判断msg是否设置有callback,如果设置了callback通过handleCallback进行处理,其实就是调用我们post的Runnable方法,否则通过handler的mCallback来处理,这个mCallback可以在构造方法中进行设置,它的原型如下:

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

如果未设置Handler的Callback ,那就通过handler的 handleMessage来处理,这个方法默认是个空实现。我们可以在创建Handler时复写这个方法。

消息的移除

public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}

public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}

public final void removeCallbacks(Runnable r)
{
    mQueue.removeMessages(this, r, null);
}

 public final void removeCallbacks(Runnable r,Object token)
{
    mQueue.removeMessages(this, r, token);
}

public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

MessageQueue中移除message的实现如下

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }

    synchronized (this) {
        Message p = mMessages;
        // Remove all messages at front.
        while (p != null && p.target == h && p.what == what
                && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

移除相应message或者runnable的逻辑都是相同的,removeCallbacksAndMessages类似,这里我们只看removeMessages的实现,满足移除必须满足的条件:

  1. 消息对象p不为null
  2. p.target即处理该消息的handler要匹配一致
  3. p.what即指定的消息类型要匹配
  4. p.obj为null或者和消息的obj匹配

其中第3个条件在移除runnable类的消息是为匹配p.callback,在同时移除message和runnable时不做匹配。