Android Handler 源码解析

447 阅读5分钟

Android Handler 源码解析

本文以 API-33 为参考

Handler的初始化

Handler对象拥有数个初始化方法,但需要的参数只有三个:Looper、Callback 以及 async。

  • Looper:将 message 发送到哪一个 Looper,由于 Looper 对象与线程绑定,可以理解为选择任务由哪一个线程来执行
  • Callback:是 Handler 类中的一个接口,传入该对象,可以自定义处理 Message 的代码
  • async:bool 值,含有该参数的构造方法是 hide 方法,也就是说,应用层正常情况下无法调用的。可以参照这篇文章:www.jianshu.com/p/5c399a27d…

Handler的初始化可以分为两个地方:子线程与主线程。初始化方式并不一样,以代码为例。

  • 主线程:
    // ....
    Handler handler = new Handler(getMainLooper());
    // ....
  • 子线程:
    private MyThread childThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...

        childThread = new MyThread();
        childThread.start();

        Handler childHandler = new Handler(childThread.childLooper);
        //...
    }

    private class MyThread extends Thread{
        public Looper childLooper;

        @Override
        public void run() {
            Looper.prepare();//创建与当前线程相关的Looper
            childLooper = Looper.myLooper();//获取当前线程的Looper对象
            Looper.loop();//调用此方法,消息才会循环处理
        }
    }

对比之下可以看到,在子线程中,必须要调用 Looper.prepare() 与 Looper.loop() 方法。根据上面的构造方法,我们知道Looper对象是 Handler 构造时需要的参数之一。我们来看两个方法的源码。

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");
    }
    // 在threadLocal中,设置一个 Looper 对象
    sThreadLocal.set(new Looper(quitAllowed));
}

public static void loop() {
    final Looper me = myLooper();
     // 进入循环,持续处理message
    for (;;) {
        // loopOnce 方法中实际处理 message 
        // 这里需要注意,当 message 被处理完之后,就会返回 false ,退出 looper 循环
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

这里先介绍一下 ThreadLocal 对象。ThreadLocal 与线程绑定,每个线程都有一个 ThreadLocal 对象。而在 prepare 方法中,是将 Looper 对象保存在 ThreadLocal 中,这代表,Looper 是与线程绑定的,而Handler 是与 Looper 绑定的。所以,Handler 与 Looper 所在的线程绑定。而 loop 方法,代表着Looper 开始工作。

根据代码阅读,我们已经知道了,Handler 需要一个 Looper 对象,但是,主线程中,我们没有做任何类似prepare 的操作,那么,主线程中,Looper 对象是何时初始化的呢?

主线程对应的对象为 ActivityThread,答案自然在 ActivityThread 对象中。在这里,也可以看到 Java 程序中大家熟悉的 main 方法。

public static void main(String[] args) {
    AndroidOs.install();
    // ....
    Looper.prepareMainLooper();

    // ....
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    // .... 
    // Looper 进入循环
    Looper.loop();
}

以上,我们可以得出结论,主线程中,在源码中已经调用了 Looper.loop() 方法,而在子线程中,需要我们手动去调用。

Handler 发送 Message

Handler 发送 Message 有很多个方法。但最终,都会调用 SendMessageAtTime 方法,具体的调用路线以及post类方法,建议阅读源码查看。

public boolean sendMessageAtTime(@NonNull 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);
}

最后,Handler 是使用 enqueueMessage 方法,将 Message 对象塞进了一个队列 MessageQueue 中。这个 MessageQueue 是什么,又是从哪里来呢?

一个持有由 Looper 分发的 Messages 链表的 low-class 。Message 不会被直接添加到 MessageQueue 中,而是会通过与 Looper 绑定的 Handler 发送。

以上是源码中对 MessageQueue 的描述。根据这个描述,我们可以猜测,MessageQueue 由 Looper 对象持有,并在 Handler 初始化时,赋予给 Handler,是 Handler 将收到的 Message 通过 MessageQueue 发送给对应的 Looper 对象。在源码中,也可以印证我们的猜测。

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

// Looper 的构造方法
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

言归正传,我们返回 enqueueMessage 方法。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

至此,Message 对象已经传入到 MessageQueue 中。这里需要注意的是,Handler 将自身作为参数放进了 Message 中。

Message 的分发与处理

Looper,经过之前的阅读,我们已经知道 Looper 在自身不断进行这一个循环。那么,Message 是如何从 Queue 中到 Loop 中呢?这之间,我们设置的 Delay 参数又是如何被处理的?

我们进入 MessageQueue 的 enqueueMessage 方法。

boolean enqueueMessage(Message msg, long when) {
    
    synchronized (this) {
        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 {
            // 在队列中插入传入的 Msg ,按照 when 参数排序
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                // 按照 when 参数排序
                if (p == null || when < p.when) {
                    break;
                }
                // 消息屏障机制,后文会详解该机制
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // Native 方法,最后会调用到 Native层Looper.wake() 方法,唤醒 Looper
        // 这里涉及到 Looper 的阻塞与唤醒机制
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

在 enqueueMessage 中,保证了 Looper 当时处于工作状态。这里涉及到Looper的阻塞以及唤醒机制,已经深入到了 Native 层,以后有机会会全面阐述。Looper 中对于 Message 处理,则在 LoopOnce 方法中。

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next(); // might block
    // .....
    Object token = null;
    long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
    try {
        // 还记得这里嘛?msg对象的 target 参数,是哪个对象? 
        msg.target.dispatchMessage(msg);
    } catch (Exception exception) {
        // .....
    } finally {
        // .....
    }

    msg.recycleUnchecked();

    return true;
}

还记得我们在 Handler 对象中,将 Message 入队时,传入的参数嘛?没错,Message 对象的 target 参数,就是发送这个 Message 的 Handler 对象。

最后,来看下 Handler 对象的 dispatchMessage 方法。

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        // 优先执行 Message 中的 Callback 对象
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

同步屏障机制

同步屏障机制是一种使得异步消息被优先快速执行,屏蔽同步消息的机制

在整个Handler机制中,并不是只有一种消息,而是有三种消息类型。

  • 同步消息
  • 异步消息
  • 同步屏障消息

我们之前在 Handler 的构造方法中,讲到了一个参数 async ,使用这个参数的构造方法,全部都被 hide 修饰 (API 28之后,可以使用 createAsync 方法),但是我们依然可以通过反射调用。当我们使用 async 为 true 构建了一个 Handler,这个 Handler 发送的消息,就是异步消息

而同步屏障消息,就是激活同步屏障机制,使得异步消息被优先处理。那么,如何发送同步屏障消息,同步屏障机制又是如何实现的呢?

发送同步屏障消息

同步屏障的发送,并不像其他类型的消息,在 Handler 中,而是在 Message Queue 中。

private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.

    synchronized (this) {
    // 这里是重点,传入了一个 Token
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        // .....
        return token;
    }
}

可以看到,这里最大的不同,是传入了一个 token 参数,并且,Message 对象没有传入 target 参数。

同步消息屏障机制的实现

要了解同步消息屏障机制的实现,需要先回忆下 Looper 中 LoopOnce 的操作。在方法中,首先调用了 MessageQueue 的 next() 方法。屏障机制的实现,主要就是在这个方法中实现的。与 Looper 实现了解耦,Looper 不负责各种消息的分辨,只是简单的分配消息。

Message next() {
    // ....
        synchronized (this) {
            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());
            }
        }
    // ....
}

核心代码如上所示,可能会产生疑惑,这也没有用到 token ,是如何判断屏障机制被激活的?这里的核心在于 msg.target == null 。之前讲过,只有同步屏障消息的target 为空。这里,就是在遍历整个链表,返回第一个异步消息。

这就达到了,屏蔽同步消息,优先处理异步消息的目的。