从 EventRunner 到 DeliQueue,一场关于「修高架」与「建枢纽」的底层路线之争
很多开发者在初次接触 HarmonyOS 的 EventHandler 与 EventRunner 时,都会产生一种强烈的既视感。毕竟,它们同样承担着在不同线程间投递 InnerEvent 或 Runnable 任务、执行延迟或定时调度的工作。如果把视线拉长,对比 Android 17 全新推出的无锁 DeliQueue,很多人心里难免会冒出一个大问号:
鸿蒙是不是一看到 Android 的新方案,就马上照抄过去了?
这个直觉很容易让人陷入「像不像」的表面判断。但如果真的深入底层,你会发现——两者走的是完全不同的两条路。
在讨论谁的方案更好之前,得先理解它们各自要解决什么问题。
Android 的旧版 MessageQueue 核心只有两个东西:
synchronized 锁:所有操作都要抢这把锁生产者 A ──→ 抢锁 → 入队 → 解锁
生产者 B ──→ 抢锁 → 入队 → 解锁
主线程 ──→ 抢锁 → 出队 → 解锁问题在哪?
其实鸿蒙早期(OpenHarmony 1.x~2.x)的消息机制也经历过类似阶段,但它的演进方向与 Android 截然不同——鸿蒙没有选择在旧模型上修补,而是直接重建了抽象层。
Android 17 交出的答卷叫 DeliQueue。为什么叫这个名字?因为它像熟食店取号一样——你拿个号就走,不用排队等,叫到你再来。
核心结构是双层的:
┌────────────────────────────────────────────┐
│ DeliQueue │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ 无锁栈 │ │ 最小堆 │ │
│ │ (Treiber Stack)│ │ (Min-heap) │ │
│ │ │ │ │ │
│ │ 生产者入队 → │ │ 主线程出队 ← │ │
│ │ CAS 原子操作 │ │ 独占访问 │ │
│ │ O(1) │ │ O(log N) │ │
│ └──────────────┘ └──────────────────┘ │
│ │
│ 墓碑机制 (Tombstone):延迟清理已取消消息 │
└────────────────────────────────────────────┘好处非常直接:
代价也很现实:
MessageQueue.mMessages 等私有字段的代码将直接失效在十六年前的 Handler/Looper 地基上,铺了一条无锁高架桥。稳,快,但路还是那条路。
鸿蒙没有继承 Android 的 Looper/Handler/MessageQueue 三层模型。它走的是一条截然不同的路——从 OpenHarmony 3.x / HarmonyOS 3.0 开始,采用 EventRunner + EventHandler 自有模型。
看看这个架构:
┌────────────────────────────────────────────┐
│ EventRunner(事件循环容器) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Event │ │ Event │ │ Event │ │
│ │ Handler A│ │ Handler B│ │ Handler C│ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ 事件队列(Queue) │ │
│ │ 优先级: IMMEDIATE > HIGH > LOW > IDLE│ │
│ └──────────────────────────────────────┘ │
│ │
│ 功能:postTask / postSyncTask / │
│ postTimingTask / removeEvent │
│ 模式:托管模式 / 手动模式 │
└────────────────────────────────────────────┘核心特点:
postTask)、同步等待 (postSyncTask)、定时执行 (postTimingTask)removeEvent、hasInnerEvent 等方法让事件管控变得极度透明不修旧路,建新枢纽。把「队列」当成一等公民,而非 Looper 的附属品。
| 设计思路 | ||
| 数据结构 | ||
| 并发模型 | ||
| 入队复杂度 | ||
| 出队复杂度 | ||
| 优先级支持 | ||
| 分布式能力 | ||
| API 兼容性 | ||
| 线程模型 | ||
| 消息生命周期 | ||
| 对旧代码影响 |
Android 17 的 DeliQueue 合并提交时间在 2024 年底至 2025 年初,而鸿蒙 EventRunner/EventHandler 架构在 OpenHarmony 3.x(2022 年~2023 年)就已经定型。
一个在 2022~2023 年就已经完成的设计,怎么可能「收到」2025 年的代码再去抄?
时间线上,鸿蒙的 EventRunner 模型甚至比 Android 的 DeliQueue 更早问世。
如果真是「抄」,应该能看到相似的数据结构、相似的内存模型、相似的并发策略。
底层数据结构、并发策略、抽象层次全都不同,何谈「抄」?
设计一个操作系统底层的消息机制,牵涉到:
指望看完几行开源代码就能「像素级复制」到另一个完全不同的系统上,是对系统工程复杂性的极大低估。
这里要做一个关键区分。
无论是用栈还是堆,用 CAS 操作还是优先级调度,只要最终目的是解决 「异步线程可靠通信」 ,工程界自然会收敛到相似的范式。
eventfd + epoll它们都在做同一件事:让一个线程优雅地处理别人发给它的任务。 这个目标本身就决定了它们必然有相似的外形——一个循环、一个队列、一个投递接口。但这不等于谁抄了谁。
好比全世界的汽车都有方向盘、油门和刹车,但不能说「特斯拉收到奔驰图纸就马上抄」——因为真正决定一辆车高度的,是底盘的调校、电机的控制算法、电池的热管理系统,而不是那几个踏板的布局。
对于 Android 开发者:
mMessages、依赖自定义 Looper/MessageQueue 监控,Android 17 会让你很难受对于鸿蒙开发者:
技术的演进就像一场旷日持久的登山运动。
面对共同的陡峭山壁——「如何让异步线程高效可靠地通信」——不同的队伍当然需要借鉴前人留下的脚印与锚点。
借鉴经验是为了少走弯路,重塑架构才是抵达顶峰的真谛。
当我们在键盘前争论「谁抄谁」的时候,不如静下心来读一读两边的源码。你会发现:有些相似是英雄所见略同,有些不同则是各有所图。
frameworks/base/core/java/android/os/ DeliQueue 实现EventRunner API 参考 (OpenHarmony 3.x+)EventHandler API 参考 (OpenHarmony 3.x+)foundation/communication/ 事件子系统 (CES) 设计futex / priority inheritance 机制说明MessageQueue.java 全局 synchronized 锁实现供对比
如果觉得本文有帮助,欢迎点赞、在看、转发。让更多人看到技术讨论应该建立在事实之上,而非直觉之上。