从 C 链表到 Android Looper:MessageQueue 的底层原理一条线讲透

前言:为什么要从 C 链表讲起?

很多 Android 开发者都用过 Handler / Looper / MessageQueue

但一深入源码就会觉得"抽象、复杂、难以下手"。

其实问题不在 Android,而在于我们没把底层模型串起来

如果你愿意退回一步,用 C 语言最基础的数据结构视角 去看,就会发现:

Android 的消息机制,本质就是:
链表 + 队列 + 排序 + 阻塞循环。

本文尝试做一件事:

👉 从 C 语言的链表出发,一步一步推导到 Android 的 MessageQueue / Looper

把这条逻辑链完整走一遍。

一、指针的本质:一切从"地址"开始

在 C 语言中:

复制代码
int x = 10;
int *p = &x;
  • x 是一个值
  • &x 是 x 的内存地址
  • p 是一个存地址的变量
  • *p 表示"通过地址访问那块内存里的值"

指针 = 存地址的变量

这是后面所有数据结构的根基。

这一点非常重要,因为:

  • 链表
  • 队列
  • MessageQueue
  • Looper

全部建立在"地址关系"之上。

二、为什么需要 struct?

单个变量无法表达复杂对象,我们需要把"相关数据"组织在一起:

复制代码
struct Person {
    int age;
    int height;
};

struct 的本质只是:

👉 一块内存的布局说明书

它本身并不负责逻辑。

三、Node:链表的最小原子结构

链表的核心是 Node

复制代码
typedef struct Node {
    void *data;        // 指向真实数据
    struct Node *next; // 指向下一个节点
} Node;

这里有两个完全不同层次的指针:

  • data业务数据指针

  • next结构关系指针

一句话总结:

Node = 数据 + 指向下一个 Node 的关系

多个 Node 通过 next 串起来,就形成了链表:

复制代码
Node1 -> Node2 -> Node3 -> NULL

四、为什么"只有 Node"是没用的?

此时会遇到一个致命问题:

👉 从哪里开始遍历?

链表必须有一个"入口",也必须有人维护整体状态。

五、Queue:Node 的管理者(系统思维的起点)

于是我们引入"管理结构":

复制代码
typedef struct {
    Node *head;
    Node *tail;
    int   size;
} Queue;

现在结构关系变成:

复制代码
Queue
 ├── head ──> Node ──> Node ──> Node ──> NULL
 ├── tail ────────────────────────────┘
 └── size

注意一个非常重要的事实:

Queue 自己不存数据,它只负责"管理 Node 的关系"。

这是从"数据结构"走向"系统设计"的第一步。

六、为什么 Queue* 不需要二级指针?

初始化 Queue 通常这样写:

复制代码
Queue q;
queue_init(&q);

void queue_init(Queue *q) {
    q->head = NULL;
    q->tail = NULL;
    q->size = 0;
}

这里:

  • q 本体已经存在(在栈上)

  • 函数只是修改 Queue 内部字段

  • 并没有修改指针变量本身

所以:

👉 一级指针足够

七、什么时候才需要二级指针?

只有一种情况:

当函数需要"创建 / 替换一个指针变量本身"

复制代码
void create_queue(Queue **pq) {
    *pq = malloc(sizeof(Queue));
}

调用方式:

复制代码
Queue *q = NULL;
create_queue(&q);

这里的本质是:

  • q 是一个 Queue* 指针变量

  • &q 的类型是 Queue**

  • Queue** 正好能"接住" &q

  • *pq = malloc(...) 本质是给 q 重新赋值

👉 二级指针的本质是"类型匹配 + 写回指针变量"

八、从 Queue 到 MessageQueue:关键差异只有一个

普通 Queue 是 FIFO

Android 的 MessageQueue 不一样,它是:

按执行时间排序的消息队列

因此 Node 演化为 Message:

复制代码
typedef struct Message {
    long when;              // 什么时候执行
    void (*callback)(void); // 要执行的任务
    struct Message *next;
} Message;

你会发现:

  • 结构没变

  • 指针没变

  • 只是数据字段更"业务化"

九、MessageQueue 的核心职责

MessageQueue 主要做三件事:

  1. when 有序插入 Message

  2. 维护单向链表

  3. 提供 next() 获取"当前可执行的消息"

它不是简单的队列,而是"时间有序链表"。

十、Looper:系统的"心跳循环"

Looper 的逻辑可以简化成一句话:

复制代码
for (;;) {
    Message *msg = queue.next();
    dispatch(msg);
}

也就是说:

Looper = 无限循环 + 从 MessageQueue 取消息并执行

这就是 Android UI 线程的"发动机"。

十一、为什么 Looper 不会空转卡死?

关键在 MessageQueue.next()

  • 如果队列为空

  • 或最近一条消息还没到执行时间

👉 线程进入阻塞状态

当:

  • 新消息入队

  • 或时间到达

👉 线程被唤醒

因此:

next() ≠ pop()
next() = "能执行才返回,否则阻塞等待"

这是系统层设计的精髓。

十二、完整映射关系一览

C 世界 Android 世界
Node Message
next 指针 Message.next
Queue MessageQueue
for(;;) Looper.loop()
阻塞等待 native poll / wake
相关推荐
感谢地心引力3 小时前
安卓、苹果手机无线投屏到Windows
android·windows·ios·智能手机·安卓·苹果·投屏
智码未来学堂4 小时前
探秘 C 语言算法之枚举:解锁解题新思路
c语言·数据结构·算法
青桔柠薯片5 小时前
数据结构:顺序表与链表
数据结构·链表
金枪不摆鳍6 小时前
算法--二叉搜索树
数据结构·c++·算法
向哆哆7 小时前
画栈 · 跨端画师接稿平台:基于 Flutter × OpenHarmony 的整体设计与数据结构解析
数据结构·flutter·开源·鸿蒙·openharmony·开源鸿蒙
季明洵7 小时前
C语言实现顺序表
数据结构·算法·c·顺序表
优雅的潮叭8 小时前
cud编程之 reduce
android·redis·缓存
2601_949613028 小时前
flutter_for_openharmony家庭药箱管理app实战+用药知识详情实现
android·javascript·flutter
一起养小猫8 小时前
Flutter for OpenHarmony 实战 表单处理与验证完整指南
android·开发语言·前端·javascript·flutter·harmonyos
2601_949975088 小时前
flutter_for_openharmony城市井盖地图app实战+附近井盖实现
android·flutter