Handler 浅析

354 阅读17分钟

Handler 作为 Android 开发岗位面试几乎必问的知识点,其重要性不言而喻

对于 Android FrameWork 源码的学习来说,对于 AMS 和 WMS 的学习来说,了解 Handler 的原理,是必不可少的

为什么 Handler 如此重要?

对于 Handler,很多的 Android 开发者就只觉得 Handler 是一个 Android SDK 中原生的线程间通信的框架

但其实 Handler 的作用远远不只是这样而已

作为 Android 开发者,一定都学习过 Java 这门语言

我们在编写 Java 代码的时候,都是通过 main() 函数运行的,但是为什么我们同样也是在 Activity 中编写 Java 代码,Activity 中并没有 main() 函数,代码却仍可以运行呢?

这是因为 Activity 的生命周期被 AMS 给托管了

AMS 是系统服务进程,AMS 进程与 App 进程之间通信的渠道就是 ActivityThread

ActivityThread.main() 函数在 App 进程初始化就会被执行

ActivityThread.main()

public static void main(String[] args)
{
        ......
        Looper.prepareMainLooper();
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null)
        {
            sMainThreadHandler = thread.getHandler();
        }
        ......
        Looper.loop();
        ......
}

ActivityThread.main() 函数中,创建了一个 Handler,初始化了主线程的 Looper,以及调用了 Looper.loop() 函数

Looper.loop()

public static void loop()
{
    ......
    for (;;)
    {
        ......
    }
    ......
}

Looper.loop() 函数中,执行了一段死循环代码

死循环?那其他的代码怎么运行?难道说......

是的,Android 中 Activity 和 Service 的生命周期其实都是基于 Handler 来进行运行的,这就是为什么面试时几乎必问 Handler

因为 Handler 不仅仅是用于线程间通信,它其实是 Android 中 Activity 和 Service 运行时的消息管理机制,因此作为 Android 开发者,Handler 是必须要弄清楚的知识点

Handler 是如何运行的?

我们平常在使用 Handler 来进行线程间通信的时候,代码几乎都是这么写的

// 主线程
Handler handler = new Handler(Looper.getMainLooper())
{
    @Override
    public void handleMessage(@NonNull Message msg)
    {
        // 处理message
        ......
    }
};

// 子线程
Thread thread = new Thread(new Runnable()
{
    @Override
    public void run()
    {
        // 发送message
        handler.sendMessage(msg);
    }
}).start();

要想搞清楚 Handler 是怎么运行的,首先得弄明白 Handler 是怎么发送 message 的

Handler.sendMessage()

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

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis)
{
    ......
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis)
{
    ......
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis)
{
    ......
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler.sendMessage() 函数最终会调用到 MessageQueue.enqueueMessage() 函数

其实 Handler 中所有的 sendXXX 函数,最终都会调用到 MessageQueue.enqueueMessage() 函数,代码太多,就不贴出来了

当调用 MessageQueue.enqueueMessage() 函数时,这个 message 就会被添加到 MessageQueue 中

Looper.loop()

public static void loop()
{
    ......
    for (;;)
    {
        Message msg = queue.next();
        ......
        msg.target.dispatchMessage(msg);
    }
    ......
}

public void dispatchMessage(@NonNull Message msg)
{
    ......
    handleMessage(msg);
}

Looper.loop() 函数的死循环中,会调用 MessageQueue.next() 函数,取出 MessageQueue 中的 message

取出后会调用 Handler.dispatchMessage() 函数,最终回调到 Handler.handleMessage() 函数,执行我们处理 message 的代码

所以 Handler 的主体运行流程就是:

未命名文件.png

Handler、Looper 和 MessageQueue 的关系是什么?

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

通过构造函数,Handler 持有了 Looper 和 MessageQueue 的对象实例

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

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

public static @Nullable Looper myLooper()
{
    return sThreadLocal.get();
}

在 Looper 的构造函数中,会创建一个 MessageQueue 的对象实例和持有当前调用的线程,说明 Looper 是和线程绑定在一起的

但是 Looper 的构造函数是私有的,所以我们只能通过 Looper.prepare() 函数来创建线程的

Looper.prepare() 函数中,会通过 sThreadLocal 来存储创建出来的 looper 对象

sThreadLocal 是一个被 static final 修饰的字段,说明 sThreadLocal 这个对象对于整个应用程序来说只有一个

可以理解为每个线程中有一个保险箱用来存储与该线程绑定的 looper 对象,而能打开这些保险箱的钥匙都是同一把钥匙

这样做可以统一管理,防止内存泄露

每个线程只能绑定一个 looper 对象,在 Looper.prepare() 函数中,如果 sThreadLocal 中已经有了一个 looper 对象,那么将会抛出一个异常

说明 每个线程会绑定一个 looper 对象,每个 looper 对象会持有一个 MessageQueue 对象

Handler、Looper 和 MessageQueue 三者间的关系好比生产者-消费者模式

Handler 是生产者

Looper 是消费者

MessageQueue 是传送带,负责存储生产者生产的消息和传递消息给消费者

MessageQueue 是什么?它的数据结构是怎样的?

MessageQueue 是一个基于链表实现的优先级队列

MessageQueue 中有一个 Message 类型的字段 mMessages,这个字段代表链表的头节点;

Message 中有一个 Message 类型的字段 next,基于这些可以看出 MessageQueue 是 基于链表结构 进行存储 message 的

MessageQueue.enqueueMessage()

boolean enqueueMessage(Message msg, long when)
{
    ......
    msg.when = when;
    Message p = mMessages;
    ......
    // 如果 MessageQueue 中没有 message 或 当前插入的 message.when = 0 或 
    // 当前插入的 message.when 小于头部 message.when
    if (p == null || when == 0 || when < p.when)
    {
        // 头插
        msg.next = p;
        mMessages = msg;
        ......
    }
    else
    {
        ......
        Message prev;
        // 遍历 MessageQueue 寻找当前 message 的插入位置
        for (;;)
        {
            prev = p;
            p = p.next;
            // 如果当前 message.when 大于等于 MessageQueue 中所有 message.when,则
            // 把当前 message 插入到 MessageQueue 的末尾
            if (p == null || when < p.when)
            {
                break;
            }
            ......
        }
        msg.next = p;
        prev.next = msg;
    }
    ......
}

MessageQueue 中存放的 message 是按照 Message.when 的大小从小到大进行排列的

整个 MessageQueue 是经过 插入排序

Message.when 代表 message 的目标处理时刻,通俗的说就是该 message 会在哪一时刻被处理

MessageQueue.next()

Message next()
{
    ......
    for (;;)
    {
        ......
        Message prevMsg = null;
        Message msg = mMessages;
        ......
        if (prevMsg != null)
        {
            prevMsg.next = msg.next;
        }
        else
        {
            mMessages = msg.next;
        }
        msg.next = null;
        msg.markInUse();
        return msg;
        ......
    }
}

当调用 MessageQueue.next() 函数取出 message 时,取出的是位于 MessageQueue 链表中的第一个 message

所以 MessageQueue 就是一个 按照 message 的目标处理时刻进行升序排列的优先级队列,排在前面的 message 优先出队

MessageQueue 的消息睡眠和消息唤醒机制

MessageQueue 的消息睡眠和消息唤醒机制其实就是阻塞队列的实现,但是 MessageQueue 实现的阻塞队列是半阻塞

为什么说 MessageQueue 实现的阻塞队列是半阻塞?是因为 MessageQueue 对于插入操作是没有做阻塞处理的

通过 MessageQueue.enqueueMessage() 函数的源码可以知道,只要调用了 MessageQueue.enqueueMessage() 函数,那么 message 是一定会被插入到 MessageQueue 中的

而普通的阻塞队列对于插入操作是会有阻塞处理的,但是 MessageQueue.enqueueMessage() 函数却没有阻塞处理,为什么?

这是因为主线程的 MessageQueue 不仅是用户在使用,同时 AMS 发送给 APP 进程的消息处理也通过其进行

如果主线程的 MessageQueue 在添加消息的时候会出现阻塞的情况,就有可能导致某些消息的缺失;如果缺失的消息还较为重要,就有可能导致系统运行错误

但是对于插入操作的不限制就会导致 MessageQueue 存在 内存溢出 的风险

不过在正常情况下,MessageQueue 是很难出现内存溢出的

/**
* 用于睡眠线程
* @param ptr 标识着一个线程,相当于线程的一个编号。
*            这个值在MessageQueue创建的时候就会通过nativeInit函数生成
* @param timeoutMillis 距离线程睡眠结束的剩余时间。0表示无需睡眠,-1表示永久睡眠。
*/
private native void nativePollOnce(long ptr, int timeoutMillis);

/**
* 用于唤醒线程
* @param ptr 标识着一个线程,相当于线程的一个编号。
*            这个值在MessageQueue创建的时候就会通过nativeInit函数生成
*/
private native static void nativeWake(long ptr);

MessageQueue 的消息睡眠和消息唤醒机制主要是通过这两个 native 函数实现的

MessageQueue.next()

Message next()
{
    ......
    int nextPollTimeoutMillis = 0;
    for (;;)
    {
        ......
        nativePollOnce(ptr, nextPollTimeoutMillis);
        ......
        final long now = SystemClock.uptimeMillis();
        Message msg = mMessages;
        ......
        if (msg != null)
        {
            // 如果还没到该message的目标处理时刻
            if (now < msg.when)
            {
                // 计算剩余时间
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, 
                                                       Integer.MAX_VALUE);
            }
            ......
        }
        else
        {
            nextPollTimeoutMillis = -1;
        }
        ......
    }
}

MessageQueue 对于取出操作的阻塞分为两种:

  • 还没到 message 的目标处理时刻,睡眠至目标处理时刻,自动唤醒
  • MessageQueue 队列为空,永久睡眠

我们可以看到,当队列为空的时候,nextPollTimeoutMillis = -1,执行下一轮循环的时候就会永久睡眠当前线程

当还没到 message 的目标处理时刻的时候,会计算 nextPollTimeoutMillis 的值,执行下一轮循环的时候就会睡眠当前线程,在经过 nextPollTimeoutMillis 数值大小的时间后,自动唤醒当前线程

唤醒已经睡眠的线程需要调用 nativeWake() 函数

在 MessageQueue 源码中调用了 nativeWake() 函数的地方主要在 MessageQueue.quit()MessageQueue.enqueueMessage() 函数中

MessageQueue.quit()

void quit(boolean safe)
{
    if (!mQuitAllowed)
    {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    ......
    if (mQuitting)
    {
        return;
    }
    mQuitting = true;
    ......
    nativeWake(mPtr);
}

MessageQueue.enqueueMessage()

boolean enqueueMessage(Message msg, long when)
{
    ......
    boolean needWake;
    // 如果MessageQueue中没有message 或 当前插入的message.when = 0 或 
    // 当前插入的message.when小于头部message.when
    if (p == null || when == 0 || when < p.when)
    {
        ......
        // 当MessageQueue已经处于睡眠状态时,mBlocked为true
        needWake = mBlocked;
    }
    else
    {
        // 如果开启了同步屏障且该message是异步消息,则需要唤醒线程
        needWake = mBlocked && p.target == null && msg.isAsynchronous();
        ......
        for (;;)
        {
            // 遍历MessageQueue寻找插入的位置
            ......
            // 如果在该异步消息的插入位置前已经有了别的异步消息,说明此时的睡眠状态
            // 是前一个异步消息的等待时刻导致的,则不唤醒线程
            if (needWake && p.isAsynchronous())
            {
                needWake = false;
            }
            ......
        }
    }
    if (needWake)
    {
        nativeWake(mPtr);
    }
}

MessageQueue 对于线程睡眠的唤醒分为以下几种:

  • 当 MessageQueue 需要中止退出的时候,唤醒线程,执行 MessageQueue.next() 函数中未执行完成的循环,命中标志位 mQuitting = true,MessageQueue.next() 函数返回 null,退出 Looper.loop() 函数中的死循环

  • 当调用 MessageQueue.enqueueMessage() 函数插入 message 时,如果 message 需要插入在 MessageQueue 中的最前面,则需要唤醒线程,让新插入的 message 先被取出

    如果这个 message 被取出后仍然没到下一个 message 的目标处理时刻,则线程继续睡眠至下一个 message 的目标处理时刻

  • 当调用 MessageQueue.enqueueMessage() 函数插入 message 时,如果 message 在 MessageQueue 的中间插入,默认不唤醒线程

  • 当调用 MessageQueue.enqueueMessage() 函数插入 message 时,如果开启了同步屏障,并且插入的这个 message 是异步消息,则需要唤醒线程让这个异步消息立刻执行

  • 当调用 MessageQueue.enqueueMessage() 函数插入 message 时,如果开启了同步屏障,并且插入的这个 message 是异步消息,但如果在该异步消息的插入位置前已经有了别的异步消息,说明此时的睡眠状态是前一个异步消息等待时刻所导致的,不需要主动唤醒线程

Handler 是如何保证线程安全的?

Handler 会管理 Activity 和 Service 运行时的消息,因此这些消息的管理必须要是线程安全的

boolean enqueueMessage(Message msg, long when)
{
    ......
    synchronized (this)
    {
        ......
    }
    return true;
}

Message next()
{
    ......
    synchronized (this)
    {
        ......
    }
    ......
}

void quit(boolean safe)
{
    ......
    synchronized (this)
    {
        ......
    }
}

这里只列举了 MessageQueue 中的三个函数,但其实 MessageQueue 中还有很多的函数中都使用了 synchronized 锁

我们知道对于一个线程来说,会唯一的绑定一个 looper 对象,每个 looper 对象会持有一个 MessageQueue 对象实例。

因此可以说,在任何时刻,每个线程所对应的 MessageQueue 的插入、取出和退出操作都是 原子性

即当 MessageQueue 的某一操作开始执行后,别的操作就无法继续进行

这样保证了 MessageQueue 的 线程安全

Handler 是怎样做到子线程-主线程之间切换的?

根据 JVM 的运行时数据区我们知道,内存是不分线程的,同一进程下的所有线程共享同一内存

对象是不分线程的,只有函数的执行需要分线程

未命名文件.png

如图所示,哪些方法是执行在子线程、哪些方法是执行在主线程的,我已经在图中标识出来了

每一个 message 对象我们可以看成一块块小的内存;每一个 MessageQueue 对象我们可以看成一块较大的内存,是许多 message 对象的内存集合

所以 Handler 不仅是一个消息管理机制,也可以看成是一个内存管理机制

因此 Handler 的实质就是 将消息封装成 message 对象,再将 message 对象的引用通过 MessageQueue 从子线程传递到主线程

Message 该如何创建?Handler 中的享元设计模式是怎么实现的?

创建 Message?那不是直接 new 吗?这是一种创建方式,但是这种方式有可能会造成我们前面说的 MessageQueue 的 内存溢出 问题

因为这种方式没有使用到 Message 的回收复用机制,也就是 Handler 中的享元设计模式

通过直接 new 的方式来创建 Message 很容易造成 OOM

不仅是因为 MessageQueue 的 内存溢出,更多的是因为 message 的多次创建容易造成 内存抖动

因为 Handler 是 Android 中 Activity 和 Service 运行时的消息管理机制,几乎每时每刻都需要创建 message

如果使用直接 new 的方式来进行创建 message,那系统运行不了多久就会出现 OOM

因为 message 对象频繁的创建和回收,会造成大量的内存碎片,当内存碎片的数量到达一定程度后,创建新的对象无法找到连续的内存,就会造成 OOM

所以创建 Message 对象推荐使用 Message.obtain() 函数,因为该函数使用到了 Message 的回收复用机制

Looper.loop()

public static void loop()
{
    ......
    for (;;)
    {
        Message msg = queue.next();
        ......
        msg.recycleUnchecked();
    }
}

Looper.loop() 函数中,每次循环的末尾会调用 Message.recycleUnchecked() 函数,这个函数的作用就是回收 message 对象

Message.recycleUnchecked()

public static final Object sPoolSync = new Object();

void recycleUnchecked()
{
    // 清空message中的所有数据
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;
    // 将该message对象加入缓存池
    synchronized (sPoolSync)
    {
        if (sPoolSize < MAX_POOL_SIZE)
        {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

Message 回收复用机制中的缓存池是一个链表结构,并且使用了 synchronized 锁,保证了缓存池的 线程安全

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

当调用 Message.obtain() 函数来创建 Message 对象时,会先从缓存池中寻找复用

只有当缓存池中没有 Message 对象的时候,才会去 new 出一个 Message 对象

Handler 的异步消息是什么?Handler 的同步屏障是什么?

对于 Android 开发者而言,Handler 的异步消息似乎大家几乎都没听说过。

其实,在 Handler 中,有三种类型的消息:

  • 普通消息 (也就是同步消息),message 的 Message.isAsynchronous() 函数返回 false
  • 异步消息,message 的 Message.isAsynchronous() 函数返回 true
  • 同步屏障消息,message 的 target 字段为 null

通过 Handler 的 sendXXX 函数插入的消息默认都是同步消息

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

mAsynchronous 这个字段在 Handler 中默认为 false,只有在 Handler 的构造函数中才能将 mAsynchronous 设置为 true

因此创建异步消息的方法有两种:

  • 创建 message 的时候调用 Message.setAsynchronous() 函数设置为 true
  • 创建 Handler 的时候将 mAsynchronous 设置为 true

但是如果 MessageQueue 没有开启 同步屏障,那么异步消息和同步消息没有任何区别,那么 同步屏障 是什么呢?

屏障的意思即为阻碍,顾名思义,同步屏障 就是阻碍同步消息,只让异步消息通过

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.target赋值,msg.target = null
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        // 按照msg.when的数值大小插入到MessageQueue中合适的位置
        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()

/**
* 用于删除同步屏障消息,关闭同步屏障
*/
public void removeSyncBarrier(int token)
{
    synchronized (this)
    {
        Message prev = null;
        Message p = mMessages;
        // 遍历MessageQueue寻找同步屏障消息的位置
        while (p != null && (p.target != null || p.arg1 != token))
        {
            prev = p;
            p = p.next;
        }
        // 如果p = null说明MessageQueue中没有同步屏障消息
        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;
        // 如果同步屏障消息的位置在MessageQueue的中间或末尾,说明同步屏障还未开启
        if (prev != null)
        {
            // 移除同步屏障消息
            prev.next = p.next;
            // 不需要唤醒线程,因为此时同步屏障还未开启,直接移除同步屏障消息即可
            needWake = false;
        }
        else // 如果同步屏障消息的位置在MessageQueue的最前面,说明同步屏障已开启
        {
            // 移除同步屏障消息
            mMessages = p.next;
            // 有两种可能:
            // 1. MessageQueue中没有异步消息,需要唤醒线程
            // 2. 异步消息等待时刻,判断MessageQueue中是否仍处于同步屏障状态
            //    2.1 不处于同步屏障状态,就唤醒线程
            //    2.2 处于同步屏障状态,就不唤醒线程
            needWake = mMessages == null || mMessages.target != null;
        }
        // 回收message
        p.recycleUnchecked();
        // MessageQueue不处于退出状态才唤醒线程
        if (needWake && !mQuitting)
        {
            nativeWake(mPtr);
        }
    }
}

MessageQueue.next()

Message next()
{
    ......
    for (;;)
    {
        ......
        nativePollOnce(ptr, nextPollTimeoutMillis);
        ......
        Message msg = mMessages;
        ......
        // MessageQueue的最前面是同步屏障消息,说明开启了同步屏障
        if (msg != null && msg.target == null)
        {
            // 退出这个do-while循环只有两种可能:
            // 1. MessageQueue遍历完都没有找到异步消息
            // 2. 找到了异步消息
            do {
                prevMsg = msg;
                msg = msg.next;
            } while (msg != null && !msg.isAsynchronous());
        }
        // 如果开启了同步屏障,这个msg一定是异步消息
        if (msg != null)
        {
            // 前面分析过了,如果没到目标处理时刻就睡眠线程
            ......
        }
        else // 如果MessageQueue中没有异步消息
        {
            // 永久睡眠线程
            nextPollTimeoutMillis = -1;
        }
        ......
    }
}

同步屏障的实现本质上 通过 target = null 的 message,在 MessageQueue.next() 函数取出 message 时,根据这个 target = null 的 message 作为标志进行判断,优先处理异步消息

需要注意的是,MessageQueue.postSyncBarrier() 函数只是将 target = null 的 message 放入 MessageQueue 中

只有当 target = null 的 message 被 MessageQueue.next() 函数取出处理时,才标志着同步屏障的开启

这也是为什么在 MessageQueue.removeSyncBarrier() 函数中,当同步屏障消息的位置在 MessageQueue 的中间或末尾时,不需要唤醒线程

假设此时的 MessageQueue 睡眠,也是由于同步消息等待时刻所导致的,不需要主动唤醒线程

MessageQueue.removeSyncBarrier() 函数中,如果同步屏障消息的位置在 MessageQueue 的最前面,说明 同步屏障 已开启,这时的唤醒线程就需要分情况讨论

通过 MessageQueue.next() 函数,我们可以知道当 同步屏障 开启后,线程睡眠的原因有两种:

  • 异步消息等待时刻,可自动唤醒睡眠

    如果线程睡眠是因为异步消息等待时刻导致的,会检查移除同步屏障消息后的 MessageQueue 是否还处于 同步屏障 状态

    • 如果不处于 同步屏障 状态,就唤醒线程,让 MessageQueue 开始轮询在该异步消息之前的同步消息

      如果该异步消息之前没有任何消息,由于此时还没到下一个消息的目标执行时刻,就接着睡眠线程等待时刻

    • 如果还处于 同步屏障 状态,就不唤醒线程,让线程继续睡眠等待时刻

  • MessageQueue 中没有异步消息,永久睡眠

    如果线程睡眠是因为 MessageQueue 中没有异步消息导致的,说明 MessageQueue 中的异步消息已经全部处理,则需要唤醒线程,关闭 同步屏障,让 MessageQueue 开始轮询同步消息

在日常的开发中,我们很少会用到 同步屏障,一般在 AMS 源码中 同步屏障 常使用,因为 AMS 的消息常常需要优先处理

例如 ViewRootImpl.scheduleTraversals() 函数中就使用到了 同步屏障,用于刷新屏幕

总结

Handler 这部分的知识其实不难,但难的是如何理解 Handler 的设计思路,将其应用到开发中

这就需要我们对于源码的阅读并不局限于只是明白代码流程,更多的应该是思考代码为什么这样设计,作用是什么