Skip to content

Android 消息机制

Posted on:March 4, 2021 at 20:51:33 GMT+8

API 30



A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper’s message queue and execute them on that Looper’s thread.

Handler 允许你发送和处理与线程 MessageQueue 关联的 Message 和 Runnable 对象。每个 Handler 实例与一个线程及该线程的 message queue 关联。但你创建一个新的 Handler,TA 与一个 Looper 绑定。Handler 将发送 message 和 runnable 给绑定 Looper 的 message queue,且在 Looper 的线程执行 message 和 runnale。

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler 有两个主要的用途:(1)调度 message 和 runnable 在未来的某个时间点执行,(2)将一个在你自己线程外执行的 action 入队。

Scheduling messages is accomplished with the post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler’s handleMessage(Message) method (requiring that you implement a subclass of Handler).

调度 message 通过 post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), 和sendMessageDelayed(Message, long)` 完成。post 版本的允许你将 Runnable 对象入队,在 message queue 收到时调用。sendMessage 版本的允许你将一个包含一些数据的 Message 对象入队,将在 Handler 的 handleMessage 方法中处理(需要你实现一个 Handler 的子类)。

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

当 Handler post 或 send 时,你可以让 TA 们在 message queue 准备好去做时处理或是指定一个被处理前的延迟或指定处理的绝对时间。后两者允许你实现 timeout,ticks 等基于时间的行为。

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler’s message queue and processed when appropriate.

当创建了一个你的应用的线程后,TA 的主线程被用于运行一个 message queue,来仔细管理顶层的应用对象(activities, broadcast receivers, etc)和 TA 们创建的窗口。你可以创建你自己的线程,通过 Handler 与应用的主线程交流。通过在你的新线程调用前述的 post 和 sendMessage 方法完成。给定的 Runnable 或 Message 将会被在 Handler 的 message queue 中调度,将在合适的时候被处理。


将 Runnable 添加到 message queue。Runnable 将会运行在 Handler attach 的线程。


Handler#sendMessage 时将会设置 Message 的 target 为 this,将 Message 与 Handler 绑定。

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

这里会把 time 设置为开机时间加上设置的 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) { = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

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

这里设置 msg.targe = this。Handler 的任务完成,每个消息的 uptimeMillis 就是延时时间。


boolean enqueueMessage(Message msg, long when) {
    if ( == null) {
        throw new IllegalArgumentException("Message must have a target.");

    synchronized (this) {
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");

        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
           + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            return false;

        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.
   = 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 && == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p =;
                if (p == null || when < p.when) {
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
   = p; // invariant: p ==
   = msg;

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

并不是所有 Message 的 target 必须不为 null。Handler 的同步屏障就是一个 target 为 null 的 msg,用来优先执行异步方法。

同步屏障一个很重要的应用就是接受垂直同步 vsync 信号,用来刷新界面。为了保证界面的流畅,每次刷新信号来时,其它任务先放一放,优先执行刷新界面的任务。

这个方法,将 msg 按照 uptimeMillis 组成一个链表,小的在前。mMessage 是头,如果为 null,将 msg 设为头。将 msg 按照 time 插到链表主要在 for 循环中,在 for 循环中找到 msg 的位置, msg.when >= p.when 的时候继续向后遍历。下面两行将 msg 插入链表。




handleMessage(msg) 默认是空方法,如果想要处理 msg 需要实现这个方法。



Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

运行线程的 message loop 的类。线程默认没有一个与 TA 们相关联的 message loop。在线程中调用 prepare 来运行 loop,调用 loop() 来处理 message 直到 loop 停止。

Most interaction with a message loop is through the Handler class.

大多数与 message loop 的交互通过 Handler 类。

This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.

这是一个 Looper 线程的实现的典型示例,使用单独的 prepare 和 loop 来创建初始的 Handler 来与 Looper 交流。

  class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {

          mHandler = new Handler(Looper.myLooper()) {
              public void handleMessage(Message msg) {
                  // process incoming messages here



在 Looper 的构造方法中初始化 MessageQueue。


调用构造方法,new 一个 Looper。将 Looper 存在 ThreadLocal 中。


先从 ThreadLocal 中获取 Looper,再根据 Looper 获得 MessageQueue。再在一个无限循环中尝试获得下一个 Message。如果有则调用 在 msg 对应 Handler 线程执行方法。



Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.

定义一个包含描述和任意数据对象的 message,可以被 Handler 发送。这个对象包含两个额外的 int 域,和一个额外的对象域,允许你在大多数情况下不分配。

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

尽管 Message的构造方法是 public 的,最好的获得一个 Message 的方法是调用 Message.obtain() 或 Handler.obtainMessage() 其中之一方法,这些方法可以从重用的对象池中得到 Message。


private static final int MAX_POOL_SIZE = 50; Message 缓存池最大 50 个。



Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.

底层类,包含被 Looper 分发的 message 列表。Message 不是直接添加到 MessageQueue 的,而是通过与 Looper 关联的 Handler 对象。

You can retrieve the MessageQueue for the current thread with Looper.myQueue().

你可以通过 Looper.myQueue() 取得当前线程的 MessageQueue。



Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;

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

        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 && == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg =;
                } 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 {

这里一个核心变量 nextPollTimeoutMillis。计算出后就调用 nativePollOnce 这个 native 方法,休眠到下一次 msg 时执行。如果在这段时间内又插入一个 msg,会唤醒线程,重新计算插入,再走一次休眠。



This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.

这个类提供 thread-local 变量。这些变量与普通的对应变量不同的是,每个访问变量的线程(通过其 get 或 set 方法)都有自己的、独立初始化的变量副本。ThreadLocal 实例通常是希望将状态与线程关联起来的类中的私有静态字段(例如,用户 ID 或事务 ID )。 例如,下面的类为每个线程生成本地的唯一标识符。线程的 id 在它第一次调用 ThreadId.get() 时就会被分配,并在后续调用中保持不变。

   import java.util.concurrent.atomic.AtomicInteger;

   public class ThreadId {
       // Atomic integer containing the next thread ID to be assigned
       private static final AtomicInteger nextId = new AtomicInteger(0);

       // Thread local variable containing each thread's ID
       private static final ThreadLocal<Integer> threadId =
           new ThreadLocal<Integer>() {
               @Override protected Integer initialValue() {
                   return nextId.getAndIncrement();

       // Returns the current thread's unique ID, assigning it if necessary
       public static int get() {
           return threadId.get();

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

每个线程持有一个对 thread-local 变量副本的隐式引用,只要 thread 是 alive 的且 ThreadLocal 实例是可访问的。在 thread 消失后,所有的 thread-local 实例都要被垃圾收集(除非存在其它对这些副本的引用)。


ThreadLocalMap 是为维护 thread local 变量的定制 hash map。key 是 ThreadLocal<?>,value 是 Object(使用时会用泛型指定类型)。


ThreadLocalMap 中的内部类,表示一个 k-v 对。

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        value = v;


根据当前线程,取得 Thread 对象中的 ThreadLocalMap 对象。接着以 ThreadLocal 实例为 key,根据 ThreadLocal 的 hash 值算出在 table 中的下标 i,即可得到 Entry。接着从 Entry 中取得 value,并转换成泛型指定的类型。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            T result = (T)e.value;
            return result;
    return setInitialValue();

e.get() : e 是一个 WeakReference,get 返回 ThreadLocal。


private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
        return getEntryAfterMiss(key, i, e);


根据当前线程,取得 Thread 对象中的 ThreadLocalMap 对象。调用 map.set 将 ThreadLocal 为 key,value 为值,执行一般的 hashmap 的 set 操作(计算 key 的 hash,更新 value 或新建 Entry。

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
        createMap(t, value);


Looper#prepare 后创建线程对应的 Looper。Looper 作为参数创建 Handler 对象,实现响应 Message 的方法。需要发送消息时,获得 Message 对象。再 Handler 发送 Message,把 Message 添加到 MessageQueue 中。Looper 在 loop 过程中知道有 MessageQueue 中消息发送来,将取出消息,调用 在 msg 对应 Handler 线程执行响应 msg 的方法。

延时 Message

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);


主线程的 Looper 无限循环为什么不会导致应用卡住?


ActivityThread#main,在 loop 之前建立 binder 通道(创建新的线程)。

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // Install selective syscall interception

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.


    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());

    // Call per-process mainline module initialization.



    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
    ActivityThread thread = new ActivityThread();
    // 建立Binder通道 (创建新线程)
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();

    if (false) {
                LogPrinter(Log.DEBUG, "ActivityThread"));

    // End of event ActivityThreadMain.

    throw new RuntimeException("Main thread loop unexpectedly exited");

无限循环是不是十分消耗 CPU 资源?

主线程的死循环一直运行会不会特别消耗 CPU 资源呢?其实不然这里就涉及到 Linux pipe/epoll 机制,简单说就是在主线程的 MessageQueue 没有消息时,便阻塞在 loop 的 中的 nativePollOnce() 方法里,此时主线程会释放 CPU 资源进入休眠状态,直到下个消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作。这里采用的 epoll 机制,是一种 IO 多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步 I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量 CPU 资源。



除了手动创建的一些子线程,Android 创建的其它线程?


熟悉 ThreadGroup 的同学会知道,在 ThreadGroup 下有两个静态成员变量,分别是systemThreadGroupmainThreadGroupmainThreadGroup其实也是systemThreadGroup的子线程组,所以我们只需要通过反射获取到systemThreadGroup对象然后递归打印就行了,代码如下:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        fun printThreads(threadGroup: ThreadGroup) {
            "group name: ${}".logI()
            // 本来想直接用反射获取子线程实例的,没想到threads被禁用了,好奇怪,源码里面明明没有@hide相关标识的
            // threadGroup::class.get<Array<Thread?>?>(threadGroup, "threads")?.filterNotNull()?.forEach { "thread name: ${}".logI() }
            arrayOfNulls<Thread?>(threadGroup.activeCount()).apply { threadGroup.enumerate(this, false) }
                .filterNotNull().forEach { "thread name: ${}".logI() }
            threadGroup::class.get<Array<ThreadGroup?>?>(threadGroup, "groups")?.filterNotNull()?.forEach { printThreads(it) }
        printThreads(ThreadGroup::class.get(null, "systemThreadGroup")!!)


I/(MainActivity.kt:34) invoke: group name: system
I/(MainActivity.kt:36) invoke: thread name: Signal Catcher
I/(MainActivity.kt:36) invoke: thread name: HeapTaskDaemon
I/(MainActivity.kt:36) invoke: thread name: ReferenceQueueDaemon
I/(MainActivity.kt:36) invoke: thread name: FinalizerDaemon
I/(MainActivity.kt:36) invoke: thread name: FinalizerWatchdogDaemon
I/(MainActivity.kt:36) invoke: thread name: Profile Saver

I/(MainActivity.kt:34) invoke: group name: main
I/(MainActivity.kt:36) invoke: thread name: main
I/(MainActivity.kt:36) invoke: thread name: Jit thread pool worker thread 0
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_1
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_2
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_3
I/(MainActivity.kt:36) invoke: thread name: Binder:26573_4
I/(MainActivity.kt:36) invoke: thread name: RenderThread
I/(MainActivity.kt:36) invoke: thread name: magnifier pixel copy result handler
I/(MainActivity.kt:36) invoke: thread name: queued-work-looper
I/(MainActivity.kt:36) invoke: thread name: DefaultDispatcher-worker-1
I/(MainActivity.kt:36) invoke: thread name: DefaultDispatcher-worker-2
I/(MainActivity.kt:36) invoke: thread name: DefaultDispatcher-worker-3

可以看到,现在进程内一共有两个线程组:system 和 main。

Signal Catcher,好像挺眼熟的,但源码中搜不到,好吧,知识盲区了,我投降。

接着往下看,有四个 Daemon 线程,随便选一个全局搜一下:


它们都在一个叫 Daemons 的类里面。找到一篇文章:


  1. HeapTaskDaemon: 用来释放堆内存的;
  2. ReferenceQueueDaemon: 一些软、弱、虚引用的对象,当它们被回收时,会被添加到对应的 ReferenceQueue 中,这个操作就由 ReferenceQueueDaemon 来负责;
  3. FinalizerDaemon: 用来回调【即将被回收的对象】的finalize方法;
  4. FinalizerWatchdogDaemon: 监视 FinalizerDaemon 线程,如果在回调【即将被回收的对象】的finalize方法时超过了100_0000_0000纳秒(即10秒),那么进程会被强行kill掉;

最后一个,Profile Saver,不知道具体是做什么用的。

main 线程组中的线程比较多:

  1. main: 不用讲都知道是主线程;
  2. *Jit thread pool worker thread 0:*不知是在哪里创建的线程池;
  3. *Binder:26573_1、Binder:26573_2、Binder:26573_3、Binder:26573_4:*Bind通讯线程;
  4. *RenderThread:*用来同步渲染 BlockingGLTextureView 的线程;
  5. *magnifier pixel copy result handler:*不知道为什么会有这个;
  6. *queued-work-looper:*这是一个 HandlerThread (自带Looper);
  7. *DefaultDispatcher-worker-123:*因为我的测试 Demo 用了协程,这几个都是 coroutines 库中的线程池;

如何处理延时 Message 消息和按顺序执行?

这里分为两步处理的,第一步是在向 MessageQueue 中会根据消息加入时间和延时时间进行排序,加入时间在前和延时时间短的 Message 排在队列的前面。第二步是在 MessageQueue 获取到要执行的 Message 之后,会判断其执行时间是否是当前,若不是,则会计算时间差,使用该时间差调用 epoll 机制进入定时睡眠。



Android消息机制-Handler · Leo’s Studio

Android中为什么主线程不会因为Looper.loop()里的死循环卡死? - 知乎

multithreading - Android default threads and their use - Stack Overflow

每日一问 | 启动了Activity 的 app 至少有几个线程?-玩Android - wanandroid.com关于 Handler 的一切

Handler是如何实现延时消息的? - 简书