核心概览
主线程(UI 线程)的消息循环是由 ActivityThread.main()
方法初始化的。它本质上是一个 无限循环,不断地从消息队列(MessageQueue)中取出消息(Message)并分发给对应的 Handler 进行处理。我们触摸屏幕、更新 UI 等操作,都是封装成消息在这个循环中执行的
建立流程的详细分解
下图清晰地展示了主线程 Looper 消息循环的建立流程,以及之后如何与AMS交互并处理消息:

下面我们来详细解读图中的每一步:
第1步:入口点 - ActivityThread.main()
正如上一个问题所讲,Zygote Fork 出子进程后,会通过反射调用 android.app.ActivityThread
类的 main
方法。这是应用进程的起点
java
// ActivityThread.java
public static void main(String[] args) {
// ... 一些初始化代码 ...
// 1. 初始化主线程的Looper
Looper.prepareMainLooper();
// ... 其他初始化 ...
// 2. 创建ActivityThread对象,并关联到AMS
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// ... 其他初始化 ...
// 3. 启动消息循环 - 这是一个无限循环,不会返回
Looper.loop();
// 理论上永远不会执行到这里
throw new RuntimeException("Main thread loop unexpectedly exited");
}
第2步:准备主Looper - Looper.prepareMainLooper()
这个方法专门用于初始化主线程的 Looper
java
// Looper.java
public static void prepareMainLooper() {
// 1. 调用重载方法,quitAllowed参数为false,表示主线程不允许退出
prepare(false);
synchronized (Looper.class) {
// 2. 确保主Looper只被初始化一次
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 3. 设置静态变量sMainLooper
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
// 4. 每个线程只能有一个Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 5. 创建Looper对象,并存入ThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
关键点:
ThreadLocal
:确保每个线程(这里是主线程)都能存取自己唯一的 Looper 实例new Looper(quitAllowed)
:在 Looper 的构造函数中,创建了消息循环的核心------MessageQueue
java
// Looper.java (构造函数)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); // 创建消息队列
mThread = Thread.currentThread();
}
第3步:启动消息循环 - Looper.loop()
这是最神奇的部分,一个"死循环"如何保证应用不卡死?
java
// Looper.java
public static void loop() {
// 1. 获取当前线程的Looper和MessageQueue
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
// 2. 无限循环开始
for (;;) {
// 3. 从消息队列中取消息,可能会阻塞
Message msg = queue.next();
if (msg == null) {
// 没有消息,Looper可能已退出
return;
}
// 4. 使用Message对应的Handler进行分发和处理
try {
msg.target.dispatchMessage(msg);
} finally {
// ...
}
// 5. 回收消息到消息池,以便复用
msg.recycleUnchecked();
}
}
关键点解析:
- 阻塞式获取消息 :
queue.next()
是核心。当消息队列为空时,这个方法会 阻塞 (进入休眠状态),释放CPU资源。直到有新的消息加入队列,它才会被唤醒并返回消息。这个阻塞-唤醒机制是由 Linux 的epoll
机制实现的,非常高效 - 消息分发 :
msg.target
就是发送这个消息的Handler
对象。dispatchMessage
方法最终会调用到我们熟悉的Handler.handleMessage(Message msg)
方法,从而在正确的线程(主线程)上执行我们的代码 - 消息复用:消息被处理后会回收到一个消息池中,这样可以避免频繁创建和销毁对象,减少内存抖动
总结与关联
- 建立时机 :消息循环是在
ActivityThread.main()
中,在应用进程创建并 attach 到 AMS 之后 建立的 - 核心三部曲 :
○Looper.prepareMainLooper()
:创建唯一的 Looper 和 MessageQueue
○ActivityThread.attach()
:与系统服务建立连接,为后续接收系统消息(如启动Activity)做准备
○Looper.loop()
:启动无限循环,开始处理消息 - 为什么不会ANR? :ANR 不是因为 loop 循环本身,而是因为单个消息的处理时间过长,导致后续的消息(例如屏幕触摸事件)得不到及时处理。这个循环本身正是保证应用响应的机制
- 与系统交互 :AMS 通过 Binder 线程发送过来的请求(如
scheduleLaunchActivity
),都会被ActivityThread
中的H
(一个 Handler)转换成Message
,然后插入主线程的MessageQueue
中,最终由主线程的Looper
取出并执行。这就实现了 从Binder线程到主线程的切换