Handler

117 阅读17分钟

是什么

Handler是Android消息机制的核心实现

与之相关的还有Looper,MessageQueue以及Message,这四个部分组成了Android的消息机制,举个不恰当又有点契合的例子,我们可以把整个消息机制想象成是传送带在传输物品:

  • Handler:放置物品和处理物品的机械手臂
  • Message:需要运输的物品
  • MessageQueue:传送带
  • Looper:运转传送带的转轴(同时还提醒机械手臂处理物品)

是不是感觉有点明白了,其实又什么都没明白?很抽象对不对?不明觉厉是不是?那换个简单直接的说法,消息机制主要有两个作用:

  1. 通过消息发送用于在未来某个时刻执行一段逻辑
  2. 处理线程之间消息传递和通信的问题

怎么用

1.怎么创建Handler对象

使用Handler的方式有两种,如下:

1.1 直接创建一个Handler对象

通过查看Handler的源码,我们可以发现,它的构造函数有好多个,除了废弃的两个和不建议App使用的,我们常用的能用的也就以下两种方式

1.1.1 创建一个指定Looper的Handler对象

// 注意,这里Looper.getMainLooper()指定的是主线程的Looper
val handler = Handler(Looper.getMainLooper())

通过这种方式创建的Handler,本身不具备处理消息的能力,消息处理需要依靠message自身的callback处理,它使用post runnable方式发送消息。看一下构造方法的源码

Handler.java

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

@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    //重点一在这里
    mLooper = looper;
    //重点二在这里
    mQueue = looper.mQueue;
    //重点三在这里
    mCallback = callback;
    mAsynchronous = async;
}

可以看到上面的构造方法中,将Looper赋值给了Handler的Looper,并将looper的mQueue赋值给了当前Handler的mQueue引用;然后是callback的赋值。这几个参数的赋值非常重要,下面会一一详细分析。

1.1.2 创建一个指定Looper的Handler对象,并构造消息的处理逻辑


val handler = Handler(Looper.getMainLooper(), object : Handler.Callback {

    override fun handleMessage(msg: Message): Boolean {
        // 对message的处理
        Timber.d("我将在未来某个时间点被执行")
        // 返回值true或false会影响消息的处理的流程
        return false or true
    }
})

这样定义的Handler,其自身是具备一定的处理消息的能力handleMessage()就是我们需要对消息进行处理的逻辑实现,其返回值也会影响消息处理流程。它的源码方法最后也是调用的public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async)方法,只是多传了一个callback参数

这里有两个地方需要注意
1.handleMessage()方法返回值的意义
2.handleMessage()具备一定的处理消息的能力,为什么是一定的,不是全部的
因为涉及后面的内容,都会挨着解释

以前可以偷懒,直接Handler()或者Handler(object: Handler.Callback{}),直接从当前线程获取Looper对象,但是现在这两个方法都被废弃了,构造方法要求指明Handler对应的Looper,为了让创建者明确自己创建这个Handler到底是在哪个Looper上,是为了往哪个MessageQueue发送消息

其他的构造方法都和async参数有关,这个涉及到异步消息和同步屏障相关的问题,通常我们是不需要使用的,源码和原理说明时再解释。

1.2 继承Handler实现它的子类

class CustomHandler(looper: Looper, callback: Callback?) : Handler(looper, callback) {

    override fun handleMessage(msg: Message) {
        // 消息处理逻辑
    }
}

继承Handler主要是为了实现handleMessage()方法进行消息处理,是不是很熟悉?Handler.Callback也有一个handleMessage()方法,这里就可以解释上面1.1.2方式创建的Handler只具备一定的处理消息的能力,因为真正意义上处理消息的应该是Handler自身的handleMessage(),然而Handler.CallbackhandleMessage()方法可以干预到Handler自身handleMessage()方法是否被执行。这里需要结合Handler源码来解释:

Handler.java

// 其他先不管,我们只需要知道消息最终的处理都会调用Handler的dispatchMessage()方法
public void dispatchMessage(@NonNull Message msg) {

    if (msg.callback != null) {
        // 如果消息的callback不为空,则以消息自身的callback处理消息
        // 这也是post runnable方式的消息处理
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // 如果创建Handler时指定了callback,则会执行callback的handleMessage方法
            // 并且其返回值,会影响Handler自身的handleMessage方法是否会被执行
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}


// handleCallback的源码实现
private static void handleCallback(Message message) {
    message.callback.run();
}

通过源码分析,我们可以看到Messagecallback拥有最高的优先级,其次是构造Handler时传入的Handler.Callback,最后才是Handler自身的handleMessage方法。通常我们可以在Handler.Callback中做一些逻辑处理判断,来决定是否将消息传递给Handler自身的handleMessage方法来处理。

以上就是几种创建Handler的方式,和不同创建方式的区别。具体需要使用哪种方式,根据自身的业务需要自行选择。

小结

Handler创建的方式有三种,分别为:

  1. 创建一个指定Looper的Handler对象
  2. 创建一个指定Looper的Handler对象并声明Handler.Callback的消息处理逻辑
  3. 继承自Handler,重写Handler的消息处理逻辑

三种创建方式,对应了不同的消息处理逻辑:

  1. 方式1使用Message自身的Callback处理消息
  2. 方式2使用Handler.Callback的handleMessage()处理消息,并决定是否拦截消息后续处理
  3. 方式3使用Handler自身的handleMessage()方法处理消息
  4. 消息处理的优先级:Message的Callback > Handler.Callback的handleMessage() > Handler的handleMessage()

2.如何使用Handler对象发送消息

有了Handler对象,接下来就是如何发送消息,常用发送消息的方式也有两种,如下:

2.1 发送message

val message = Message.obtain().apply {
    // 设置消息携带的数据
    what = 1
    data = Bundle()
}
handler.sendMessage(message)

发送Message主要有两点:
①.构造消息
②.发送消息

2.1.1 构造消息

通常说到构造,我们第一反应应该是new一个Message对象,这个方式确实可行,但是消息机制中提供了更好的构造消息的方式,就是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
            // 消息缓存池-1
            sPoolSize--;
            return m;
        }
    }
    // 如果消息缓存池为空,才会重新创建消息
    return new Message();
}

如上代码所示,使用消息缓存池,可以更好的复用消息对象,减少对象创建和内存分配。其他和消息缓存池、消息缓存容量大小相关的后面流程分析再说。

至此,我们构造了一条Message,现在将其发送出去。

2.1.2 发送消息的完整流程

通常我们会使用sendMessage方法将消息发送出去,那发送给了谁?是怎么发送的?,首先看代码

Handler.java

// 发送消息的初始方法
public final boolean sendMessage(@NonNull Message msg) {
    // 调用的是延时发送消息,并且延时为0,表示消息立即发送并期望立即被处理
    return sendMessageDelayed(msg, 0);
}


// 延时发送消息
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    // 这里需要注意(SystemClock.uptimeMillis() + delayMillis)这个参数,表示的含义
    // SystemClock.uptimeMillis()为上一次系统开机至今的时间,是一个相对时间
    // 这个参数表示的意义即:当前消息将在下一刻或者未来某个时刻被执行处理
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

// 在某个指定时间发送消息
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);
}


// 消息进入消息队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
    // 注意这里,将当前Handler指定为消息的target,表示这条消息由当前Handler进行处理
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    // 如果构造Handler对象时,设置了async为true,此后该Handler发送的所有消息都将为异步消息
    // 通常不会建议我们自己频发发送异步消息,因为会扰乱系统异步消息使用同步屏障
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 调用MessageQueue的消息入队方法
    return queue.enqueueMessage(msg, uptimeMillis);
}

到这里MessageQueue就出场了,我们可以把它称为消息队列,其内部维护了一个Message的单向链表,所有Message都是往它里面塞,然后从它里面被取出来。关于MessageQueue是怎么来的,后面说Looper的时候会细说,我们现在只需要知道在创建Handler的时候不是需要指定Looper么,指定Looper的过程也会将Looper对应的MessageQueue赋值给当前Handler的queue引用,上面分析构造函数的源码时也做了重点说明,这里再贴一下:


public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    // 这里将looper的mQueue赋值给了Handler的mQueue
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

接下来我们继续看MessageQueueenqueueMessage方法:

boolean enqueueMessage(Message msg, long when) {
    // 上一步设置target将在此处被检查,保证消息有Handler将其处理
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    synchronized (this) {
        // 检查消息的状态是否可用
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        // 检查Looper是否已经退出
        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;
        }

        // 标记message正在使用中
        msg.markInUse();
        // 设置消息将要被执行的时间,相对时间(开机时间+延时时间)
        msg.when = when;
        // 取出当前MessageQueue消息队列的头节点
        Message p = mMessages;
        // 是否需要唤醒是根据当前消息队列是否有值以及mBlocked的值来决定的
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // 其实上面源代码的注释已经说的很清楚了,我们自己再分析一下判断条件的意思
            // p == null 表示当前消息队列是空的,没有任何消息节点
            // when == 0 表示msg是开机时创建并期望被立即执行处理
            // when < p.when 表示msg的期望执行处理时间比头结点消息期望执行处理时间更早
            // 综上,这三种情况下msg都将成为当前消息队列的头结点
            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.
            // 这里需要注意一下 p.target == null 和 msg.isAsynchronous() 条件,这都是同步屏障消息的必要条件
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 循环的意义是为了找到msg插入的正确位置
            // 判断条件就when的值,期望被执行处理的时间(开机时间+延时时间)
            for (;;) {
                // 将头结点赋值给prev
                prev = p;
                // 头结点的下一个节点赋值给自身
                // 这个其实就是双指针遍历链表
                p = p.next;
                if (p == null || when < p.when) {
                    // p == null 表示已经遍历到队尾最后一个消息了
                    // when < p.when 表示已经找到了比msg执行时间更晚的消息了
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            // 将当前msg插入到队列中
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            // 如果当前Looper是休眠状态的,则需要唤醒Looper的循环继续取消息
            nativeWake(mPtr);
        }
    }
    return true;
}

代码很长,注释已经清楚的描述了每个步骤做了什么操作,至此,我们已经明白sendMessage()到底干了啥
①发送消息的整个操作是将msg插入到当前消息队列中
②插入队列前设置了消息期望被处理的时间when,这个是个相对时间(开机时间+延时时间),目的是确定msg入队的正确位置,确保消息能够在当前或未来某个时间被执行
③插入队列之前设置了target,确保当前msg是能够被指定的Handler处理的
④消息入队的各个判断条件是非常关键的,决定了msg应该插入哪个位置,什么时候能够被执行
⑤关于异步消息和同步屏障相关的太复杂了,放在后面说

2.2 发送Runnable

除了sendMessage()方法发送一条msg,我们也可以使用handler.post()方法发送一个runnable来实现消息发送

// 创建一个Runnable
val runnable = Runnable{
    // 消息处理逻辑
}
// 使用post方法发送runnable
handler.post(runnable)

那发送runnable又是怎么回事?继续上源代码

Handler.java

public final boolean post(@NonNull Runnable r) {
   // sendMessageDelayed这个很熟悉了,上面才看了
   // 主要看看getPostMessage()
   return  sendMessageDelayed(getPostMessage(r), 0);
}

// 也是熟悉的实现,只是给获取的message指定了callback
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

看到这里就很清晰了,其实所谓的发送消息和post runnable,本质上都是发送Message,结合上面我们创建Handler的几种方式,可以得出以下结论:

  • post(runnable) 则直接指定了message的callback,最后消息将被这个runnable(callback)直接处理。
  • sendMessage() 则根据消息自身是否指定callback,随后依据消息处理的优先级被处理。

3 消息入队后是怎么被处理的

上面我们提到过Handler的dispatchMessage方法会处理Message,那dispatchMessage()是在哪里触发的?消息是怎么从MessageQueue里面取出来的?

这个时候就要看Looper的了,Looper是个啥?最开始我们举了个例子,说它是运转传送带的转轴,也可以理解为是整个消息机制的动力源,是它不停地在消息队列中取出消息给Handler进行处理。有了这个感性的认知后,我们再稍微记一下几个概念,先有个概念,后面会对这些概念进行解释:
①一个线程中只会存在一个Looper
②没有Looper的线程Handler是无法创建的
③Looper在执行一个死循环不停的取出MessageQueue里的消息

我们在使用Handler的时候没有关心过Looper的创建和方法调用,依然能够接受并处理消息

其实是因为ActivityThread已经帮我们处理好了,来看看Android主线程启动入口方法里面的一些操作

ActivityThread.java

public static void main(String[] args) {
    ......
    // 屏蔽了一些暂时不需要关注的代码

    // 重点1在这里,初始化主线程的Looper
    Looper.prepareMainLooper();

    // 这里就是创建我们主线程的地方了
    ActivityThread thread = new ActivityThread();
    // 初始化仪表类、Application、ViewRootImpl回调配置等
    thread.attach(false, startSeq);

    ......
    
    // 重点2在这里,开启主线程Looper循环
    Looper.loop();

    // 重点3在这里,执行到这里居然会抛出异常
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

最主要的是Looper.prepareMainLooper()Looper.loop()方法。至于抛出异常的问题,后面说明。

Looper.java

// 这里是重点1
// 今天才看到这个方法过时了 - -!,推荐直接使用prepare()
@Deprecated
public static void prepareMainLooper() {
    // 这里就是创建Looper的核心方法
    prepare(false);
    synchronized (Looper.class) {
        // sMainLooper用于储存主线程的Looper,只能被初始化一次,即ActivtyThread中
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

// 这里是真正对线程赋值Looper的实现
private static void prepare(boolean quitAllowed) {
    // 这里也就解释了上面①为什么一个线程里面只能存在一个Looper
    // 因为Looper对象是存储在线程局部变量中的,实现了线程隔离
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 给当前线程绑定上Looper对象
    sThreadLocal.set(new Looper(quitAllowed));
}

// 取出当前ThreadLocal中设置的Looper,即prepare中创建的Looper对象
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

通过源码分析,我们可以看到,prepareMainLooper()方法主要是创建了一个Looper对象,并且使用ThreadLocal将其保存了起来,这里也印证了 "为什么一个线程里面只能存在一个Looper",看到了Looper的创建,接下来我们再看看Looper的启动

Looper.java

// 这里是重点二
// 开启Looper循环,即从MessageQueue里面取消息出来
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }

    ......

    for (;;) {
        // 注意这里,如果loopOnce()返回了false,则整个loop循环都将退出
        // 所以我们需要关注一下loopOnce()什么时候会返回false
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}


// 从messageQueue中取出消息并处理
private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) {
    // MessageQueue的next方法又是一个非常非常重要的方法
    Message msg = me.mQueue.next(); // might block
    if (msg == null) {
        // 这里我们看到如果msg == null loopOnce()就会返回false
        // 其实正常情况下,msg是不会为null的
        // 因为消息队列中没有消息的时候,我们的Looper就休眠了
        // 知道重新有消息入队,会唤醒Looper又开始取消息
        return false;
    }

    .....
    // 我们照例屏蔽了一些与主流程无关的代码
    
    try {
        // 还记得发送消息时,默认会给message设置target么
        // 其实就是设置的发送message的handler,用于消息的处理
        // 现在就到了它起作用的时候
        msg.target.dispatchMessage(msg);
    } catch (Exception exception) {
        throw exception;
    } finally {
        ......
    }
    
    // 回收消息至消息池中,用于下次复用
    msg.recycleUnchecked();

    return true;
}

至此,我们已经为主线程绑定上了Looper,并且开启了loop循环,开始读取MessageQueue中的消息并进行处理。我们再来着重分析一下loopOnce中的实现细节,它其实做了两件事情:
①从消息队列中取出消息,MessageQueuenext()方法
②将取出来的消息进行处理,target(即Handler)的dispatchMessage()方法

接下来我们就需要看看MessageQueue的next方法了

MessageQueue.java

Message next() {
    // mPtr字段是由native调用和赋值的,一般情况下不会为0
    // 根据翻译可以知道在消息循环已经退出时,或者程序在退出后尝试重启时,这个值会为0
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    
    // 消息队列为空时,需要休眠的时间
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 重点1在这里,关于没有消息时looper不会退出的终极秘密
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // 重点2在这里,Handler的同步屏障
                // 注意msg.target == null,这个条件就表明
                // 此类型的消息并不是我们可以发送的,都是系统在进行处理
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
                // 通过循环查找消息队列中的第一条异步消息,并让该异步消息的下一条消息立即被处理
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 如果消息的期望被执行时间>当前时间,则设置一个唤醒时间用于唤醒Looper
                    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;
            }

            // 当MessageQueue调用了退出方法,mQuitting会为true
            // 故正常情况下不会执行该退出逻辑
            if (mQuitting) {
                dispose();
                return null;
            }
            ......
        }
        ......
}

通过源码分析,next()方法的主要作用就是取出消息,如果当前消息队列中没有消息或者当前消息执行时间还没有到的时候,则会调用nativePollOnce()方法,让Looper进行休眠,直到有消息可以返回时又会唤醒Looper继续从MessageQueue的next方法来取消息。上面看MessageQueue的enqueueMessage()方法时,注释了nativeWake(mPtr)方法,就是它在唤醒Looper。

有朋友会说,next()方法中不是有两个地方可能会返回null吗?,其实正常情况是不存在执行返回null的条件的,上面注释也说明了,一个mPtr,一个mQuitting,如果真到了执行该逻辑的情况也是Looper循环退出的时候,还考虑消息怎么处理干嘛。如果是主Looper退出就是程序退出的时候了,啥也不用干了。

  • 重点1的说明:关于nativePollOnce()方法的作用,简单来说就是让Looper进入休眠,并设置一个休眠时间,休眠结束后唤醒Looper继续取消息,它背后对应了native层的NativeLooperNativeLooper是通过Linux的epoll机制来实现的休眠唤醒机制。在早期的源码里面,Looper休眠使用的是Java的wait(),但是因为性能问题后面换成了Linux的epoll。其具体实现细节,可参见具体的源码,也可以参考大佬的文章
  • 关于重点2的说明:同步屏障是Handler机制里面非常重要,隐藏得很深的一个知识点。它的主要作用是提供给系统处理紧急任务消息的。

到这一步,我们通过调用链和源码分析,已经初步搞清楚了Handler在应用层的整个运行过程和实现细节。

关于Message的补充说明

Message作为Handler消息机制中的消息载体,它的结构其实很简单

public final class Message implements Parcelable {
    // 通常用作消息类型声明
    public int what;

    // arg1、arg2这两个参数通常用于传递一些数值
    public int arg1;
    public int arg2;

    // 传递任意对象,注意如果是跨进程通讯时,需要obj为可序列化的
    public Object obj;

    // 消息期望被执行的时间
    public long when;
    
    // Bundle数据
    Bundle data;
    
    // 此条消息将被哪个Handler处理
    Handler target;

    // 游戏被处理时优先级最高的callback
    Runnable callback;
    
    // 下一条消息的引用,瞧这玩意儿就是个链表
    Message next;
    
    // 消息池同步锁
    public static final Object sPoolSync = new Object();
    // 消息池,即链表的头节点
    private static Message sPool;
    private static int sPoolSize = 0;

    // 消息池最大消息容量
    private static final int MAX_POOL_SIZE = 50;
}

Handle的其他知识点

在子线程中使用Handler

在子线程中是不能够直接创建Handler的,需要做一些前置处理。我们需要在子线程中先创建并准备好Looper,并将其绑定到线程当中,随后才能创建Handler发送消息。记得要调用Looper的loop()方法让整个Looper跑起来

使用IdleHandler做点性能优化

IdleHandler可以作为性能优化的一种手段,将一些不紧急但是必要的任务放在IdleHandler中执行,能够起到很好的优化作用。

上面我们再看MessageQueue的next()方法的时候,其实后面还有一截内容给忽略掉了,如下

MessageQueue.java

Message next() {
    int pendingIdleHandlerCount = -1; // -1 only during first iteration

    for (;;) {
        // 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.
        // 这是处理调用IdleHandler的操作,有几个条件
        // 1、pendingIdleHandlerCount < 0表明:只会在此for循环里执行一次处理IdleHandler操作,初始pendingIdleHandlerCount=-1,最后初始pendingIdleHandlerCount=0
        // 2、当前消息队列为空(mMessages == null)
        // 3、还没有到发下一消息将要被处理的时间(now < mMessages.when)
        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++) {
        // 取出当前的idleHandler
        final IdleHandler idler = mPendingIdleHandlers[i];
        // 释放数组中对当前idleHandler的引用
        mPendingIdleHandlers[i] = null;

        boolean keep = false;
        try {
            // 处理idleHandler,留意这里queueIdle()的返回值
            keep = idler.queueIdle();
        } catch (Throwable t) {
            Log.wtf(TAG, "IdleHandler threw exception", t);
        }

        if (!keep) {
            synchronized (this) {
                // 如果queueIdle()返回值为false,IdleHandler才会被在被处理后移除
                // 如果queueIdle()返回值为true,则每次for循环,当前IdleHandler都有可能被处理
                mIdleHandlers.remove(idler);
            }
        }
    }

    // Reset the idle handler count to 0 so we do not run them again.
    pendingIdleHandlerCount = 0;
}
    

最后看一下,IdleHandler是什么,以及怎么使用

/**
 * Callback interface for discovering when a thread is going to block
 * waiting for more messages.
 */
public static interface IdleHandler {
    /**
     * Called when the message queue has run out of messages and will now
     * wait for more.  Return true to keep your idle handler active, false
     * to have it removed.  This may be called if there are still messages
     * pending in the queue, but they are all scheduled to be dispatched
     * after the current time.
     */
    boolean queueIdle();
}

可以看到IdleHandler就是一个接口,包含一个queueIdle方法,它并不是Handler的子类。所以,我们只需要继承这个接口,并实现其queueIdle()方法,最后调用MessageQueue的addIdleHandler添加到MessageQueue中即可。在不需要的时候也可以调用removeIdleHandler方法将其移除

MessageQueue

public void addIdleHandler(@NonNull IdleHandler handler) {
    if (handler == null) {
        throw new NullPointerException("Can't add a null IdleHandler");
    }
    synchronized (this) {
        mIdleHandlers.add(handler);
    }
}


public void removeIdleHandler(@NonNull IdleHandler handler) {
    synchronized (this) {
        mIdleHandlers.remove(handler);
    }
}

与Handler相关的知识点

HandlerThread

ThreadLocal线程隔离

内存泄漏以及处理

特别鸣谢

Android源码

Handler Looper MQ(eventfd & epoll)

敲敲代码啦大佬的回答