Android-Framework-03-Handler-原理

1. Handler 的核心作用

  1. 消息传递:在不同线程间传递消息(Message)。
  2. 任务调度:将任务(Runnable)延迟或立即执行。
  3. 线程通信:通过消息队列和 Looper 实现线程间通信。

Handler 的核心工作流程基于以下三个组件:

  • MessageQueue:消息队列,负责存储消息。
  • Looper:线程的消息循环,负责从队列中取消息并分发。
  • Handler:消息发送和处理的桥梁。

2. 核心源码模块

以下是 Handler 相关模块的源码组成及其主要职责:

(1) Handler 类

位置android.os.Handler

  • 负责发送消息(sendMessage())和处理消息(handleMessage())。

  • 核心属性和方法:

    • Looper mLooper:与 Handler 绑定的线程的 Looper。
    • MessageQueue mQueue:与 Looper 关联的消息队列。
    • Callback mCallback :可选的回调接口,用于替代 handleMessage()
    • sendMessage() 系列方法:用于向消息队列发送消息。
    • dispatchMessage() :分发消息到 handleMessage()Runnable

(2) Message 类

位置android.os.Message

  • 封装消息的数据结构,包含:

    • what:消息标识。
    • obj:附加对象。
    • arg1/arg2:附加整型数据。
    • target:指向接收消息的 Handler。
    • when:消息触发时间(延迟执行时使用)。

(3) MessageQueue 类

位置android.os.MessageQueue

  • 消息队列,负责管理消息的存取。

  • 核心属性和方法:

    • enqueueMessage() :将消息插入队列。
    • next() :获取队列中的下一条消息(阻塞操作)。
    • removeMessages() :从队列中移除消息。

(4) Looper 类

位置android.os.Looper

  • 消息循环,负责从 MessageQueue 中取出消息并分发。

  • 核心属性和方法:

    • prepare() :初始化当前线程的 Looper 和 MessageQueue。
    • loop() :启动消息循环。
    • myLooper() :获取当前线程的 Looper 实例。

3. Handler 的整体架构关系

Handler 源码整体可以分为以下三个部分:

(1) 消息发送流程

  1. 调用 Handler 的 sendMessage()

    • Message 添加到与 Looper 关联的 MessageQueue。
    • 如果是延迟消息,MessageQueue 按时间顺序插入消息。
  2. MessageQueue 的 enqueueMessage()

    • 将消息插入队列,并唤醒可能正在等待的 Looper。

(2) 消息处理流程

  1. Looper 的 loop() 开启消息循环

    • 不断调用 MessageQueue 的 next() 方法,获取下一条消息。
    • 如果消息为空,阻塞线程,等待新消息。
  2. Handler 的 dispatchMessage() 处理消息

    • 检查消息的 Callback,优先执行 Runnable。
    • 如果没有 Callback,则调用 handleMessage() 进行处理。

(3) 消息生命周期管理

  • 消息创建

    • 通过 Message.obtain() 创建,复用 Message 对象以减少内存分配开销。
  • 消息移除

    • Handler 提供方法(如 removeCallbacksAndMessages())移除特定类型的消息,防止内存泄漏或意外调用。

4. 核心流程源码解析

4.1 三个组件主要函数和源码

  • Handler:主要函数
  • Looper源码分析

ThreadLocal 线程隔离工具类

  • MessageQueue:主要函数

MessageQueue.enqueueMessage()向消息队列添加消息

MessageQueue.next()从消息队列中获取消息使用for循环

  • MessageQueue源码分析

入队:根据时间排序,当队列满的时候,阻塞,直到用户通过next取出消息。当next方法被调用,通知MessagQueue可以进行消息的入队。 出队:由Looper.loop(),启动轮询器,对queue进行轮询。当消息达到执行时间就取出来。当message queue为空的时候,队列阻塞,等消息队列调用enqueuer Message的时候,通知队列,可以取出消息,停止阻塞。

出队:由Looper.loop(),启动轮询器,对queue进行轮询。当消息达到执行时间就取出来。当message queue为空的时候,队列阻塞,等消息队列调用enqueuer Message的时候,通知队列,可以取出消息,停止阻塞

4.2 跨线程原理:

Handler 跨线程通信的实现依赖于以下几个关键机制:

(1) Looper 和线程绑定

  • 每个线程(默认情况下,只有主线程)可以通过调用 Looper.prepare() 创建一个 Looper 和对应的 MessageQueue

  • Looper

    • 负责在当前线程中启动消息循环。
    • 与当前线程一一绑定,确保线程安全。
csharp 复制代码
java
复制代码
public static void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(new MessageQueue()));
}

关键点

  • 每个线程只能有一个 Looper。
  • Looper 和线程通过 ThreadLocal 绑定,保证线程独立性。

(2) MessageQueue 的线程安全

  • MessageQueue 是线程间通信的核心数据结构,存储消息队列。

  • Handler 的 sendMessage() 将消息插入目标线程的 MessageQueue。

  • 队列操作

    • 插入消息时,使用同步锁(synchronized)确保线程安全。
    • 取消息时,通过 阻塞等待next())避免 CPU 空转。

(3) Handler 的线程间引用

Handler 与它的 Looper 绑定,并通过 Looper 的 MessageQueue 实现跨线程通信:

  1. Handler 在线程 A 创建

    • 当 Handler 创建时,它绑定的 Looper 通常属于另一个线程(例如主线程)。
    • 通过目标线程的 Looper,将消息发送到目标线程的 MessageQueue。
  2. Handler 负责发送和分发消息

    • sendMessage() 方法将消息对象的 target 设置为当前 Handler。
    • 消息进入 MessageQueue 后,最终由目标线程的 Looper 分发回对应 Handler。

(4) Looper 的消息循环

目标线程的 Looper 不断运行 loop() 方法,从 MessageQueue 中读取消息并分发:

  • 每个消息都有一个目标 Handler,通过 msg.target.dispatchMessage() 分发给目标 Handler 处理。
csharp 复制代码
java
复制代码
public static void loop() {
    for (;;) {
        Message msg = queue.next(); // 阻塞,等待消息
        if (msg == null) {
            return; // 退出消息循环
        }
        msg.target.dispatchMessage(msg); // 分发到 Handler
    }
}

跨线程的关键

  • 消息的发送和处理发生在不同线程,但操作通过线程安全的 MessageQueue 和 Looper 完成。

4.3. 跨线程的完整执行流程

以下是跨线程通信的完整执行流程:

  1. 线程 A(发送消息的线程)

    • 调用 Handler.sendMessage()Handler.post()
    • 将消息添加到线程 B 的 MessageQueue。
  2. 线程 B(接收消息的线程)

    • 线程 B 的 Looper 不断从 MessageQueue 中取出消息。
    • 调用消息的 target(即 Handler)的 dispatchMessage() 方法。
    • 最终在线程 B 的上下文中执行任务。

4.4. 关键代码解析

(1) Handler 发送消息

arduino 复制代码
java
复制代码
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue; // 获取绑定的 MessageQueue
    if (queue == null) {
        throw new RuntimeException("Handler is not attached to a Looper");
    }
    msg.target = this; // 将目标 Handler 设置为自己
    msg.when = uptimeMillis; // 设置触发时间
    return queue.enqueueMessage(msg, uptimeMillis); // 插入队列
}

(2) MessageQueue 的插入操作

ini 复制代码
java
复制代码
boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        msg.when = when;
        Message p = mMessages;
        if (p == null || when < p.when) {
            msg.next = p;
            mMessages = msg; // 插入队列头
        } else {
            // 遍历队列,找到合适位置插入
            Message prev;
            while (p != null && when >= p.when) {
                prev = p;
                p = p.next;
            }
            msg.next = p;
            prev.next = msg;
        }
        notify(); // 唤醒等待的 Looper
    }
    return true;
}

(3) Looper 消息分发

java 复制代码
java
复制代码
public static void loop() {
    MessageQueue queue = myLooper().mQueue;
    for (;;) {
        Message msg = queue.next(); // 可能阻塞
        if (msg == null) return;

        try {
            msg.target.dispatchMessage(msg); // 分发消息到 Handler
        } finally {
            msg.recycle(); // 回收消息
        }
    }
}

(4) Handler 的消息处理

scss 复制代码
java
复制代码
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        msg.callback.run(); // 执行 Runnable
    } else {
        handleMessage(msg); // 执行用户定义的消息处理逻辑
    }
}

4.5. 结论

Handler 跨线程通信的核心机制是通过 LooperMessageQueue 的线程绑定特性实现的:

  1. 发送线程:通过 Handler 将消息插入目标线程的消息队列。
  2. 接收线程:目标线程的 Looper 负责从消息队列中取出消息,并分发给 Handler 处理。
  3. 线程安全 :通过 线程独立的 Looper同步锁,确保消息队列的线程安全性。

5. Handler 在 Native层的调用

MQ 无消息时,会进入 nativePollOnce() 休眠,此时无消息,处于休眠状态 MQ 有消息时,会立即通过 nativeWake() 唤醒去处理消息.

在 Android 的 Native 层,nativePollOnceNativeWake 是与消息循环和事件处理相关的核心方法,通常用于 NativeMessageQueueLooper 的实现中。它们的主要职责是完成消息等待和唤醒的工作,支持线程间的高效通信。

以下是它们的详细解析:


5.1. nativePollOnce

(1) 定义与功能

nativePollOnce 是一个 阻塞式的事件等待机制 ,用于从当前线程的 Native 消息队列中轮询事件。它类似于 Java 层的 MessageQueue.next(),但在 Native 层中实现。

(2) 典型的调用流程

当线程需要等待事件时,会调用 nativePollOnce。它会阻塞线程,直到:

  • 有新事件可处理
  • 超时(timeout)发生
  • 被显式唤醒 (通过 NativeWake)。

(3) 底层实现

  • epoll :Native 层使用 epoll_wait 等机制等待文件描述符事件。
  • 管道(pipe)机制:通常使用一个管道(pipe)或事件文件描述符(eventfd)来实现线程的唤醒。

以下是简化的伪代码:

scss 复制代码
cpp
复制代码
int nativePollOnce(int timeoutMillis) {
    int result = epoll_wait(epoll_fd, events, MAX_EVENTS, timeoutMillis);
    if (result > 0) {
        // 处理就绪事件
        for (int i = 0; i < result; ++i) {
            handleEvent(events[i]);
        }
    } else if (result == 0) {
        // 超时
    } else {
        // 出现错误
    }
    return result;
}

(4) 参数

  • timeoutMillis:等待的超时时间(单位:毫秒)。

    • -1 表示无限等待。
    • 0 表示立即返回(非阻塞)。

(5) 返回值

  • 大于 0:返回处理的事件数量。
  • 等于 0:表示超时。
  • 小于 0:表示错误。

5.2. NativeWake

(1) 定义与功能

NativeWake 是用于 显式唤醒阻塞线程 的方法,通常用于通知某些事件已经准备好,使线程能够退出 nativePollOnce 的阻塞状态并继续执行。

(2) 工作机制

NativeWake 的实现通常通过 写入管道或触发事件文件描述符 来实现唤醒:

  1. 在线程阻塞等待(epoll_waitpoll)时,管道的一端会被监听。
  2. 调用 NativeWake 时,向管道写入数据或触发事件。
  3. 等待的线程检测到管道事件后,退出阻塞状态,开始处理事件。

伪代码实现:

csharp 复制代码
cpp
复制代码
void NativeWake() {
    const char wakeSignal = 1;  // 唤醒信号
    write(wake_pipe_fd[1], &wakeSignal, sizeof(wakeSignal));
}

(3) 典型应用

  • 消息队列中的新任务:当向队列中添加任务时,需要唤醒阻塞的线程。
  • 退出线程循环:用于通知线程退出事件循环。
  • 中断等待:需要打断当前的等待状态时使用。

5.3. 二者配合使用

nativePollOnceNativeWake 通常成对出现,用于实现高效的线程间通信。

以下是一个消息队列循环的简化实现:

scss 复制代码
cpp
复制代码
// 管道用于唤醒机制
int wake_pipe_fd[2];

// 初始化管道和 epoll
void init() {
    pipe(wake_pipe_fd);  // 创建管道
    epoll_fd = epoll_create(1);

    // 将管道的读端注册到 epoll
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = wake_pipe_fd[0];
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, wake_pipe_fd[0], &event);
}

// 等待事件
void loop() {
    while (!stop) {
        int result = nativePollOnce(-1);  // 无限等待
        if (result > 0) {
            // 处理事件
        }
    }
}

// 唤醒线程
void wake() {
    NativeWake();
}

5.4. epoll

5.5. Java 层与 Native 层的桥接

在 Android 的消息机制中,Java 层的 MessageQueue 和 Native 层的消息队列通过 JNI 桥接:

  • MessageQueue.nativePollOnce():Java 层调用 Native 方法等待消息。
  • MessageQueue.nativeWake():Java 层调用 Native 方法唤醒线程。

对应的 JNI 实现:

scss 复制代码
cpp
复制代码
JNIEXPORT void JNICALL
Java_android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jint timeoutMillis) {
    nativePollOnce(timeoutMillis);
}

JNIEXPORT void JNICALL
Java_android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj) {
    NativeWake();
}

5.6. 实际应用场景

  1. 主线程的消息循环

    • 主线程通过 nativePollOnce 阻塞等待消息,Java 层的 HandlerMessageQueue 添加新消息时通过 NativeWake 唤醒主线程。
  2. 线程池的任务调度

    • 线程池中的工作线程在等待新任务时使用 nativePollOnce,当有新任务时通过 NativeWake 唤醒线程处理任务。
  3. 高性能事件驱动框架

    • 使用 nativePollOnceNativeWake 实现高效的事件循环和线程间通信。

5.7. 结论

  • nativePollOnce :用于阻塞等待 Native 层的消息队列事件,基于 epoll_wait 等底层机制。
  • NativeWake :用于显式唤醒阻塞线程,打破 nativePollOnce 的等待状态。
  • 二者的结合为 Android 提供了高效的线程间通信机制,是消息处理和事件循环的核心。

6. 消息同步屏障机制

其实同步屏障对于我们的日常使用的话其实是没有多大用处。因为设置同步屏障和创建异步Handler的方法都是标志为hide,说明谷歌不想要我们去使用他

文章主要内容是:先介绍什么同步屏障,再分析如何使用以及正确地使用。

6.1 什么是同步屏障机制

同步屏障机制是一套为了让某些特殊的消息得以更快被执行的机制

注意这里我在同步屏障之后加上了机制二字,原因是单纯的同步屏障并不起作用,他需要和其他的Handler组件配合才能发挥作用。

这里我们假设一个场景:我们向主线程发送了一个UI绘制操作Message,而此时消息队列中的消息非常多,那么这个Message的处理可能会得到延迟,绘制不及时造成界面卡顿。同步屏障机制的作用,是让这个绘制消息得以越过其他的消息,优先被执行。

MessageQueue中的Message,有一个变量isAsynchronous,他标志了这个Message是否是异步消息;标记为true称为异步消息,标记为false称为同步消息。同时还有另一个变量target,标志了这个Message最终由哪个Handler处理。

我们知道每一个Message在被插入到MessageQueue中的时候,会强制其target属性不能为null,如下代码:

arduino 复制代码
MessageQueue.class

boolean enqueueMessage(Message msg, long when) {
  // Hanlder不允许为空
  if (msg.target == null) {
      throw new IllegalArgumentException("Message must have a target.");
  }
  ...
}
复制代码

而android提供了另外一个方法来插入一个特殊的消息,强行让target==null

ini 复制代码
private int postSyncBarrier(long when) {
    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;
        // 把当前需要执行的Message全部执行
        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;
    }
}
复制代码

代码有点长,重点在于:没有给Message赋值target属性,且插入到Message队列头部 。当然源码中还涉及到延迟消息,我们暂时不关心。这个target==null的特殊Message就是同步屏障

MessageQueue在获取下一个Message的时候,如果碰到了同步屏障,那么不会取出这个同步屏障,而是会遍历后续的Message,找到第一个异步消息取出并返回。这里跳过了所有的同步消息,直接执行异步消息。为什么叫同步屏障?因为它可以屏蔽掉同步消息,优先执行异步消息。

我们来看看源码是怎么实现的:

ini 复制代码
Message next() {
    ···
    if (msg != null && msg.target == null) {
        // 同步屏障,找到下一个异步消息
        do {
            prevMsg = msg;
            msg = msg.next;
        } while (msg != null && !msg.isAsynchronous());
    }
    ···
}
复制代码

如果遇到同步屏障,那么会循环遍历整个链表找到标记为异步消息的Message,即isAsynchronous返回true,其他的消息会直接忽视,那么这样异步消息,就会提前被执行了。

注意,同步屏障不会自动移除,使用完成之后需要手动进行移除,不然会造成同步消息无法被处理。我们可以看一下源码:

ini 复制代码
Message next() {
    ...
    // 阻塞时间
    int nextPollTimeoutMillis = 0;
    for (;;) {
        // 阻塞对应时间 
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // 同步屏障,找到下一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            // 如果上面有同步屏障,但却没找到异步消息,
            // 那么msg会循环到链表尾,也就是msg==null
            if (msg != null) {
                ···
            } else {
                // 没有消息,进入阻塞状态
                nextPollTimeoutMillis = -1;
            }
            ···
        }
    }
}
复制代码

可以看到如果没有即时移除同步屏障,他会一直存在且不会执行同步消息。因此使用完成之后必须即时移除。但我们无需操心这个,后面就知道了。

6.2 如何发送异步消息

上面我们了解到了同步屏障的作用,但是会发现postSyncBarrier方法被标记为@hide,也就是我们无法调用这个方法。那,讲了这么多有什么用?

咳咳~不要慌,但我们可以发异步消息啊。在系统添加同步屏障的时候,不就可以趁机上车了,是吧。

添加异步消息有两种办法:

  • 使用异步类型的Handler发送的全部Message都是异步的
  • 给Message标志异步

给Message标记异步是比较简单的,通过setAsynchronous方法即可。

Handler有一系列带Boolean类型的参数的构造器,这个参数就是决定是否是异步Handler:

less 复制代码
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    // 这里赋值
    mAsynchronous = async;
}
复制代码

在发送消息的时候就会给Message赋值:

less 复制代码
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);
}
复制代码

但是异步类型的Handler构造器是标记为hide,我们无法使用,但在api28之后添加了两个重要的方法:

less 复制代码
public static Handler createAsync(@NonNull Looper looper) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    return new Handler(looper, null, true);
}

public static Handler createAsync(@NonNull Looper looper, @NonNull Callback callback) {
    if (looper == null) throw new NullPointerException("looper must not be null");
    if (callback == null) throw new NullPointerException("callback must not be null");
    return new Handler(looper, callback, true);
}
复制代码

通过这两个api就可以创建异步Handler了,而异步Handler发出来的消息则全是异步的。

typescript 复制代码
public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}
复制代码

6.3 如何正确使用

上面我们似乎漏了一个问题:系统什么时候添加同步屏障?

异步消息需要同步屏障的辅助,但同步屏障我们无法手动添加,因此了解系统何时添加和删除同步屏障是非常必要的。只有这样,才能更好地运用异步消息这个功能,知道为什么要用和如何用

了解同步屏障需要简单了解一点屏幕刷新机制的内容。放心,只需要了解一丢丢就可以了。

我们的手机屏幕刷新频率有不同的类型,60Hz、120Hz等。60Hz表示屏幕在一秒内刷新60次,也就是每隔16.6ms刷新一次。屏幕会在每次刷新的时候发出一个 VSYNC 信号,通知CPU进行绘制计算。具体到我们的代码中,可以认为就是执行onMesure()onLayout()onDraw()这些方法。好了,大概了解这么多就可以了。

了解过 view 绘制原理的读者应该知道,view绘制的起点是在 viewRootImpl.requestLayout() 方法开始,这个方法会去执行上面的三大绘制任务,就是测量布局绘制。但是,重点来了:

调用requestLayout()方法之后,并不会马上开始进行绘制任务,而是会给主线程设置一个同步屏障,并设置 ASYNC 信号监听。 当 ASYNC 信号的到来,会发送一个异步消息到主线程Handler,执行我们上一步设置的绘制监听任务,并移除同步屏障

这里我们只需要明确一个情况:调用requestLayout()方法之后会设置一个同步屏障,知道ASYNC信号到来才会执行绘制任务并移除同步屏障。(这里涉及到Android屏幕刷新以及绘制原理更多的内容,本文不详细展开,感兴趣的读者可以点击文末的连接阅读。)

那,这样在等待ASYNC信号的时候主线程什么事都没干?是的。这样的好处是:保证在ASYNC信号到来之时,绘制任务可以被及时执行,不会造成界面卡顿。但这样也带来了相对应的代价:

  • 我们的同步消息最多可能被延迟一帧的时间,也就是16ms,才会被执行
  • 主线程Looper造成过大的压力,在VSYNC信号到来之时,才集中处理所有消息

改善这个问题办法就是:使用异步消息。当我们发送异步消息到MessageQueue中时,在等待VSYNC期间也可以执行我们的任务,让我们设置的任务可以更快得被执行且减少主线程Looper的压力。

可能有读者会觉得,异步消息机制本身就是为了避免界面卡顿,那我们直接使用异步消息,会不会有隐患?这里我们需要思考一下,什么情况的异步消息会造成界面卡顿:异步消息任务执行过长、异步消息海量。

如果异步消息执行时间太长,那即时是同步任务,也会造成界面卡顿,这点应该都很好理解。其次,若异步消息海量到达影响界面绘制,那么即使是同步任务,也是会导致界面卡顿的;原因是MessageQueue是一个链表结构,海量的消息会导致遍历速度下降,也会影响异步消息的执行效率。所以我们应该注意的一点是:

不可在主线程执行重量级任务,无论异步还是同步

那,我们以后岂不是可以直接使用异步Handler来取代同步Handler了?是,也不是。

同步Handler有一个特点是会遵循与绘制任务的顺序,设置同步屏障之后,会等待绘制任务完成,才会执行同步任务;而异步任务与绘制任务的先后顺序无法保证,在等待VSYNC的期间可能被执行,也有可能在绘制完成之后执行。因此,我的建议是:如果需要保证与绘制任务的顺序,使用同步Handler;其他,使用异步Handler

7. 总结

Handler 的源码框架体现了 Android 消息机制的高效设计:

  1. 消息的发送与分发解耦

    • Handler 负责消息的创建和发送。
    • Looper 和 MessageQueue 负责消息的存储与调度。
  2. 线程安全

    • Handler 与 MessageQueue 的通信通过 synchronized 机制确保线程安全。
  3. 内存管理

    • Message 的复用机制减少了内存开销。
  4. 灵活性

    • 支持延迟消息、优先级控制、Runnable 等多种方式。

Handler 通过这些核心组件和流程,使得 Android 系统在多线程环境下实现了简单、高效的消息传递与任务调度。

相关推荐
zzhongcy38 分钟前
复合索引 (item1, item2, item3 ) > (?, ?, ?) 不起作用,EXPLAIN 后type=ALL(全表扫描)
android·数据库
冬奇Lab1 小时前
稳定性性能系列之十三——CPU与I/O性能优化:Simpleperf与存储优化实战
android·性能优化
像风一样自由2 小时前
android native 中的函数动态注册方式总结
android·java·服务器·安卓逆向分析·native函数动态注册·.so文件分析
nono牛2 小时前
Makefile中打印变量
android
魅影骑士00102 小时前
柯里化函数
后端·设计模式
没有了遇见3 小时前
Android 关于RecycleView和ViewPager2去除边缘反馈
android
城东米粉儿3 小时前
android gzip数据压缩 笔记
android
城东米粉儿3 小时前
android 流量优化笔记
android
似霰4 小时前
HIDL Hal 开发笔记10----添加硬件访问服务(Java 层调用 HIDL)
android·framework·hal
佛系打工仔6 小时前
绘制K线第三章:拖拽功能实现
android·前端·ios