前言:为什么要从 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 主要做三件事:
-
按
when有序插入 Message -
维护单向链表
-
提供
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 |