在学习 Android 消息机制时,总会遇到这些名词:Looper、Handler、MessageQueue。虽然在开发中经常使用它们,比如用 Handler 切线程、延迟任务、更新 UI,但也常常会有一些疑惑:
- Looper 到底是什么?为什么主线程一定要有它?
- 消息队列是如何工作的?
- 这些机制背后到底是怎么实现的?
仅仅停留在“用得会”远远不够,要想真正掌握 Android 的线程模型,就必须深入理解 Looper 的概念与原理。 因此,本篇从基础概念入手,先弄清楚消息机制的作用和结构设计初衷,再通过源码追踪分析其运行流程。
什么是“消息队列”?为什么主线程需要它?
消息队列的概念
消息队列(MessageQueue) 是一个线程内部的任务缓冲区,按时间顺序存储将要被执行的“消息”(Message),等待轮询机制取出并处理。
每个消息代表一个待执行的任务,如:
- 按钮点击事件
View重绘请求Handler发出的消息- 系统广播、生命周期回调等
这些消息是异步提交的,而消息队列保证它们按序被处理。
为什么主线程需要消息队列?
Android 的主线程(也叫 UI 线程)承担着用户交互和界面更新的重任,因此有几个硬性要求:
- 主线程必须保持“活跃” :不能执行完任务就退出。
- 主线程不能阻塞:一旦卡顿,界面无法响应,导致 ANR。
- 事件处理要有顺序:例如点击按钮后应该先处理点击事件,再刷新 UI。
- 不能抢占执行:任务之间不能互相打断,需按顺序调度。
🧠 这正是“消息队列 + Looper”派上用场的地方:
- 消息队列缓存所有任务;
- Looper 轮询消息队列,一次只处理一个消息,确保线程安全;
- Handler 提供接口,支持跨线程提交消息,解耦业务逻辑和线程控制。
所以,主线程需要一个消息驱动机制来:
- 驱动应用正常运转;
- 排队管理事件;
- 保持线程活跃但不高占用;
- 实现线程间安全通信。
类比解释:主线程像一个“消息处理员”
想象主线程是一个前台接待员:
- 客人(消息)排队进入前台(消息队列)
- 前台有个循环机制(Looper),一个个接待
- 客人有不同需求(消息类型)
- 每个需求都交给对应的处理者(Handler)
你不能让这个接待员一次同时接待 10 个人,也不能让他下班就不干了,所以:
- 要有队列(顺序性)
- 要有调度(轮询)
- 要有流程控制(派发处理)
原理理解
整体流程图
graph TD
subgraph "主线程 Thread"
A[Looper] --> B[MessageQueue]
D[Handler] --> B
B --> D
D --> E[handleMessage]
end
subgraph "应用层调用"
F[sendMessage] --> D
end
subgraph "消息循环"
G[Looper.loop] --> B
B -->|next取出Message| H[Message]
H -->|msg.target.dispatchMessage| D
end
- Looper:管理消息循环。
- MessageQueue:消息队列,保存所有待处理的消息。
- Message:封装具体任务和数据。
- Handler:用于发送和处理消息。
主线程的 Looper 初始化过程
public static void main(String[] args) {
...
Looper.prepareMainLooper(); // 创建主线程 Looper
...
Looper.loop(); // 开启主线程消息循环
}
Looper.prepareMainLooper()
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
最终调用 prepare() 创建 Looper 并绑定到当前线程:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
每个线程只能有一个 Looper,存储在ThreadLocal<Looper>中。
Looper.loop() 源码解析
public static void loop() {
...
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
...
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
...
} finally {
...
}
msg.recycleUnchecked();
return true;
}
queue.next()会阻塞直到有消息可处理。msg.target是 Handler 实例,通过dispatchMessage()回调开发者的handleMessage()。- 消息处理完成后回收 Message。
MessageQueue 原理
消息入队(enqueueMessage)
Handler.sendMessage()
-> Handler.sendMessageDelayed()
-> Handler.sendMessageAtTime()
-> MessageQueue.enqueueMessage():
java
boolean enqueueMessage(Message msg, long when) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
...
return true;
}
插入链表,按时间顺序排序
消息出队(next)
Message next() {
for (;;) {
...
Message msg = mMessages;
if (msg != null && msg.when <= now) {
// 时间到了,取出处理
mMessages = msg.next;
return msg;
}
// 阻塞等待(使用 native 层 epoll 等)
nativePollOnce(ptr, nextPollTimeoutMillis);
}
}
Handler:连接消息与业务逻辑
Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
handler.sendMessage(Message.obtain(...));
核心方法 dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
msg.callback.run();
} else {
handleMessage(msg);
}
}
如果 Message 携带了 Runnable,则直接执行;否则走 handleMessage()。
常见问题
为什么在线程里,直接创建 Handler 会崩溃?
因为线程默认没有 Looper,而 Handler 创建时需要绑定一个 Looper,否则会抛出异常:
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
}
在线程中正确使用 Handler 的两种方式
方向一:手动指定 Looper(通常是主线程的 Looper)
适用于:子线程中想向主线程发送消息
Handler mainHandler = new Handler(Looper.getMainLooper());
方向二:在子线程中创建 Looper 和 Handler
适用于:希望子线程拥有自己的消息循环能力(即:让子线程变成一个“消息处理线程”)
new Thread(() -> {
Looper.prepare(); // 为该线程创建 Looper 和 MessageQueue
Handler threadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("ThreadHandler", "线程内处理消息: " + msg.what);
}
};
threadHandler.sendEmptyMessage(1); // 发消息给自己处理(也可以外部调用)
Looper.loop(); // 启动消息循环
}).start();
或者使用系统封装的 HandlerThread(推荐方式),内部自动创建和管理Looper:
HandlerThread handlerThread = new HandlerThread("WorkerThread");
handlerThread.start();
Handler threadHandler = new Handler(handlerThread.getLooper());
threadHandler.post(() -> {
Log.d("HandlerThread", "执行异步任务");
});