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 系统在多线程环境下实现了简单、高效的消息传递与任务调度。

相关推荐
ac-er88882 小时前
Yii框架中的队列:如何实现异步操作
android·开发语言·php
流氓也是种气质 _Cookie4 小时前
uniapp 在线更新应用
android·uniapp
zhangphil6 小时前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲6 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥7 小时前
python操作mysql
android·python
Couvrir洪荒猛兽8 小时前
Android实训十 数据存储和访问
android
等一场春雨9 小时前
Java设计模式 八 适配器模式 (Adapter Pattern)
java·设计模式·适配器模式
五味香10 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
晚秋贰拾伍11 小时前
设计模式的艺术-命令模式
运维·设计模式·运维开发·命令模式·开闭原则
十二测试录11 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化