Looper 创建与启动
📌 面试重要度:⭐⭐⭐⭐⭐
考察频率:字节 95% | 阿里 92% | 腾讯 90%
一、核心概念
1.1 定义与作用
一句话定义: Looper 是 Android 消息机制的循环器,负责在线程中创建消息队列并不断循环处理消息,每个线程最多只能有一个 Looper 实例,通过 ThreadLocal 机制实现线程隔离。
为什么重要:
- Android 架构核心:主线程的所有生命周期回调、UI 更新都依赖主线程的 Looper
- 面试必考:Looper 创建流程、prepare/loop 方法、主线程 Looper 是所有 Android 面试必问的基础知识
- 线程通信基础:理解 Looper 创建才能掌握如何在子线程中使用 Handler
- 源码分析入门:Looper 代码简洁清晰,是学习 Android Framework 源码的最佳入口
1.2 与其他概念的关系
Looper 是 Handler 消息机制四大核心组件之一(Handler、Message、MessageQueue、Looper),与其他组件的关系:
- 与 MessageQueue:Looper 创建时自动创建 MessageQueue,一一对应关系
- 与 Handler:Handler 创建时必须关联一个 Looper,从而与其 MessageQueue 绑定
- 与 ThreadLocal:Looper 通过 ThreadLocal 实现每个线程独立存储(详见
./03-ThreadLocal机制.md) - 与 loop() 循环:创建和启动是准备阶段,循环机制是运行阶段(详见
./02-Looper.loop()循环机制.md)
边界说明: 本文专注于 Looper 的创建过程(prepare 方法)和启动过程(loop 方法调用),不深入循环内部细节和 ThreadLocal 实现原理。
二、核心原理
2.1 Looper 创建流程
2.1.1 整体流程概览
Looper 的创建分为三种场景,对应三个不同的入口方法:
场景1:主线程 Looper 创建
ActivityThread.main()
↓
Looper.prepareMainLooper() // 主线程专用,不允许退出
↓
prepare(false) // quitAllowed = false
↓
new Looper(false)
↓
sThreadLocal.set(looper)
场景2:子线程 Looper 创建
new Thread(() -> {
Looper.prepare() // 子线程手动调用
↓
prepare(true) // quitAllowed = true
↓
new Looper(true)
↓
sThreadLocal.set(looper)
// ... 使用 Handler
Looper.loop() // 开启循环
}).start()
场景3:HandlerThread 自动创建
HandlerThread thread = new HandlerThread("name")
thread.start()
↓
HandlerThread.run()
↓
Looper.prepare() // 内部自动调用
↓
Looper.loop() // 内部自动调用
关键设计:
- 线程隔离:通过 ThreadLocal 实现每个线程独立的 Looper
- 单例保证:同一线程多次调用 prepare() 会抛出异常
- 主线程特殊处理:主线程 Looper 不允许退出(quitAllowed = false)
- 自动创建 MessageQueue:Looper 构造函数中自动创建对应的消息队列
2.2 源码分析
2.2.1 主线程 Looper 创建
主线程 Looper 在应用启动时由 ActivityThread.main() 自动创建:
// 【Android 12】frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// 安装选择性的系统调用拦截器
AndroidOs.install();
// CloseGuard 默认开启
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// 设置进程的用户
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
// ★关键步骤1:创建主线程 Looper
Looper.prepareMainLooper();
// ...其他初始化代码
// 创建 ActivityThread 实例
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// 获取主线程 Handler(H)
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// ★关键步骤2:启动消息循环
Looper.loop();
// 如果 loop() 退出,说明主线程异常,抛出错误
throw new RuntimeException("Main thread loop unexpectedly exited");
}
关键点:
- 自动创建:开发者无需手动调用,系统自动完成
- 创建时机:在 ActivityThread 实例化之前,确保后续代码可以使用主线程 Handler
- 异常保护:loop() 正常情况下永不返回,如果返回说明发生严重错误
2.2.2 prepareMainLooper() 源码
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
// 主线程 Looper 静态变量,全局唯一
private static Looper sMainLooper;
/**
* 初始化当前线程为主线程的 Looper
* 主线程 Looper 由 Android 环境创建,应用代码不应该调用此方法
*/
public static void prepareMainLooper() {
// 调用 prepare,传入 quitAllowed = false(不允许退出)
prepare(false);
synchronized (Looper.class) {
// ★检查是否已经创建过主线程 Looper
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// ★保存主线程 Looper 到静态变量
sMainLooper = myLooper();
}
}
/**
* 获取主线程 Looper(全局可访问)
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
关键设计:
- 单例保护:通过 synchronized 和 sMainLooper 检查确保只创建一次
- 全局访问:任何线程都可以通过
Looper.getMainLooper()获取主线程 Looper - 不可退出:传入
quitAllowed = false,调用 quit() 会抛出异常
2.2.3 prepare() 核心创建逻辑
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
// ThreadLocal 存储每个线程的 Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
/**
* 为当前线程初始化 Looper
* 子线程使用 Handler 前必须调用此方法
*/
public static void prepare() {
prepare(true); // 子线程允许退出
}
private static void prepare(boolean quitAllowed) {
// ★检查当前线程是否已经有 Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// ★创建 Looper 并存入 ThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
/**
* 获取当前线程的 Looper
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
关键点:
- ThreadLocal 机制:每个线程独立存储自己的 Looper,互不干扰(详细原理见
./03-ThreadLocal机制.md) - 重复检查:如果线程已有 Looper,抛出异常防止覆盖
- 可配置退出:通过 quitAllowed 参数控制是否允许调用 quit()
2.2.4 Looper 构造函数
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
// Looper 对应的消息队列
final MessageQueue mQueue;
// 当前线程
final Thread mThread;
// 是否允许退出
private final boolean mAllowQuit;
private Looper(boolean quitAllowed) {
// ★创建消息队列
mQueue = new MessageQueue(quitAllowed);
// ★记录当前线程
mThread = Thread.currentThread();
// ★设置是否允许退出
mAllowQuit = quitAllowed;
}
关键设计:
- 自动创建 MessageQueue:Looper 和 MessageQueue 一一对应,构造时自动创建
- 记录线程信息:保存创建 Looper 的线程引用,用于检查调用是否合法
- final 修饰:所有成员变量都是 final,创建后不可变
MessageQueue 构造函数:
// 【Android 12】frameworks/base/core/java/android/os/MessageQueue.java
// Native 层 MessageQueue 的指针
private long mPtr;
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
// ★调用 Native 方法创建 Native 层的 NativeMessageQueue
mPtr = nativeInit();
}
private native static long nativeInit();
Native 层创建:
// 【Android 12】frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
// 创建 Native 层 MessageQueue
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL) {
// 获取当前线程的 Looper(Native 层)
mLooper = Looper::getForThread();
if (mLooper == NULL) {
// 如果不存在,创建新的 Native Looper
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
关键点:
- Java + Native 双层结构:Java 层 MessageQueue 对应 Native 层 NativeMessageQueue
- Native Looper:Native 层也有独立的 Looper,用于处理 Native 消息和 epoll 机制
- 跨语言通信:mPtr 保存 Native 对象指针,用于 JNI 调用
2.3 Looper 启动流程
2.3.1 loop() 方法入口
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
/**
* 在当前线程中运行消息队列循环
* 调用此方法后会阻塞,直到调用 quit()
*/
public static void loop() {
// ★获取当前线程的 Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 检查线程身份(Binder 调用相关)
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// 标记进入循环状态
final MessageQueue queue = me.mQueue;
// 清除 Binder 调用者的身份标识
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 允许使用系统属性覆盖阈值
int thresholdOverride = SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
// ★核心:无限循环(详细内容见 ./02-Looper.loop()循环机制.md)
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return; // 队列退出,结束循环
}
}
}
关键点:
- 前置检查:确保当前线程已经调用 prepare() 创建了 Looper
- 无限循环:
for(;;)持续运行,除非队列退出 - 循环细节:具体的消息处理逻辑在
./02-Looper.loop()循环机制.md中详细讲解
2.3.2 启动时机对比
| 场景 | prepare() 调用位置 | loop() 调用位置 | 谁负责调用 |
|---|---|---|---|
| 主线程 | ActivityThread.main() 开头 | ActivityThread.main() 结尾 | 系统自动 |
| 子线程手动 | 线程 run() 方法开头 | 创建 Handler 后 | 开发者手动 |
| HandlerThread | HandlerThread.run() 开头 | HandlerThread.run() 中 | HandlerThread 自动 |
2.4 重要细节与边界条件
细节1:为什么主线程 Looper 不允许退出?
// 【Android 12】frameworks/base/core/java/android/os/MessageQueue.java
void quit(boolean safe) {
// ★主线程 Looper 的 mQuitAllowed = false
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// 唤醒消息循环,让其检测到 mQuitting = true 后退出
nativeWake(mPtr);
}
}
原因分析:
- 主线程依赖:所有 Activity 生命周期、UI 更新、输入事件都通过主线程 Looper 处理
- 退出后果:主线程 Looper 退出会导致应用无法响应任何事件,等同于应用崩溃
- 保护机制:系统通过
quitAllowed = false防止开发者误操作
细节2:子线程为什么必须手动调用 prepare()?
// ❌ 错误示例:子线程未调用 prepare()
new Thread(() -> {
Handler handler = new Handler(); // 抛出异常!
handler.post(() -> {
// ...
});
}).start();
// ✅ 正确示例:手动准备 Looper
new Thread(() -> {
Looper.prepare(); // ★必须先调用
Handler handler = new Handler();
Looper.loop(); // ★开启循环
}).start();
原因:
// 【Android 12】frameworks/base/core/java/android/os/Handler.java
public Handler(@Nullable Callback callback, boolean async) {
// ...
// ★获取当前线程的 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
// ★如果为 null,说明未调用 prepare(),抛出异常
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
mIsShared = false;
}
设计思想:
- 主线程是特殊的,系统保证一定有 Looper
- 子线程是可选的,开发者按需创建 Looper
- 显式调用 prepare() 可以让开发者明确知道线程会持续运行(直到 quit)
细节3:一个线程可以创建多个 Looper 吗?
// ❌ 错误示例:同一线程多次调用 prepare()
Looper.prepare();
Looper.prepare(); // ★抛出异常:"Only one Looper may be created per thread"
源码保护:
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));
}
原因:
- 设计原则:一个线程只需要一个消息队列,多个 MessageQueue 无意义且容易混乱
- ThreadLocal 限制:ThreadLocal 存储的是单个对象,不支持多个 Looper
- 避免资源浪费:每个 Looper 都会创建 Native 层对象(epoll 文件描述符等),重复创建浪费资源
细节4:prepareMainLooper() 可以在其他线程调用吗?
// ❌ 错误理解:在子线程调用 prepareMainLooper()
new Thread(() -> {
Looper.prepareMainLooper(); // ★会覆盖主线程 Looper 吗?
}).start();
实际行为:
public static void prepareMainLooper() {
prepare(false); // 为当前线程创建 Looper
synchronized (Looper.class) {
if (sMainLooper != null) {
// ★主线程已经调用过,会抛出异常
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 将当前线程的 Looper 设为主线程 Looper(不会发生)
}
}
结论:
prepareMainLooper()应该且只能由主线程调用- 子线程调用会抛出 "The main Looper has already been prepared." 异常
sMainLooper静态变量在应用启动时就已设置,不可更改
三、实际应用
3.1 典型场景
场景1:子线程中使用 Handler
需求:在子线程中执行耗时任务,并在该线程内部处理回调消息
实现方式:
public class WorkerThread extends Thread {
private Handler mHandler;
@Override
public void run() {
// ★步骤1:准备 Looper
Looper.prepare();
// ★步骤2:创建 Handler(此时 Looper 已存在)
mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
// 在子线程中处理消息
switch (msg.what) {
case MSG_TASK:
performTask();
break;
}
}
};
// ★步骤3:开启消息循环
Looper.loop();
}
public Handler getHandler() {
return mHandler;
}
private void performTask() {
// 执行耗时任务
}
}
// 使用
WorkerThread thread = new WorkerThread();
thread.start();
// 等待 Handler 创建完成
thread.getHandler().sendEmptyMessage(MSG_TASK);
注意事项:
- Handler 获取时机:必须等待 run() 执行到 Handler 创建后才能获取,否则为 null
- 线程生命周期:loop() 会阻塞线程,直到调用 quit() 才结束
- 资源释放:不再使用时调用
Looper.myLooper().quit()退出循环
场景2:使用 HandlerThread(推荐)
需求:同场景1,但希望简化代码
实现方式:
// HandlerThread 内部已封装 prepare() 和 loop()
HandlerThread handlerThread = new HandlerThread("WorkerThread");
handlerThread.start();
// 直接获取 Looper 创建 Handler
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
// 在 HandlerThread 线程中处理
}
};
// 使用完毕后退出
handlerThread.quit();
HandlerThread 内部实现:
// 【Android 12】frameworks/base/core/java/android/os/HandlerThread.java
public class HandlerThread extends Thread {
private Looper mLooper;
@Override
public void run() {
mTid = Process.myTid();
// ★自动调用 prepare()
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); // 通知 getLooper() 可以返回了
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
// ★自动调用 loop()
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 等待 Looper 创建完成
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
}
优势:
- 自动准备:无需手动调用 prepare() 和 loop()
- 安全获取:getLooper() 内部等待机制确保 Looper 创建完成
- 生命周期管理:提供 quit() 和 quitSafely() 方法
场景3:在任意线程向主线程发送消息
需求:子线程完成任务后更新 UI
实现方式:
// 获取主线程 Looper 创建 Handler
Handler mainHandler = new Handler(Looper.getMainLooper());
// 在子线程中使用
new Thread(() -> {
// 执行耗时操作
String result = loadDataFromNetwork();
// 切换到主线程更新 UI
mainHandler.post(() -> {
textView.setText(result); // 在主线程执行
});
}).start();
关键点:
- Looper.getMainLooper():全局方法,任何线程都可以调用
- 线程切换:消息在 Handler 绑定的 Looper 所在线程执行(主线程)
- 无需准备:主线程 Looper 已经由系统创建并运行,直接使用即可
3.2 最佳实践
✅ 推荐做法
1. 使用 HandlerThread 代替手动管理
// ✅ 推荐
HandlerThread thread = new HandlerThread("MyThread");
thread.start();
Handler handler = new Handler(thread.getLooper());
// ❌ 不推荐:手动管理容易出错
new Thread(() -> {
Looper.prepare();
// ...
Looper.loop();
}).start();
2. 正确获取子线程 Handler
// ✅ 推荐:使用 HandlerThread 的同步机制
HandlerThread thread = new HandlerThread("MyThread");
thread.start();
Handler handler = new Handler(thread.getLooper()); // getLooper() 会等待创建完成
// ❌ 错误:可能在 prepare() 前获取
WorkerThread thread = new WorkerThread();
thread.start();
Handler handler = thread.getHandler(); // 可能为 null!
3. 及时释放资源
// ✅ 推荐:Activity 销毁时退出 HandlerThread
@Override
protected void onDestroy() {
super.onDestroy();
if (handlerThread != null) {
handlerThread.quitSafely(); // 安全退出(处理完当前消息)
// 或 handlerThread.quit(); // 立即退出(清空队列)
}
}
4. 主线程 Handler 无需指定 Looper
// ✅ 推荐:在主线程创建 Handler
Handler handler = new Handler(); // 自动使用当前线程(主线程)的 Looper
// ❌ 多余:显式指定主线程 Looper(除非在子线程中创建)
Handler handler = new Handler(Looper.getMainLooper());
❌ 常见错误
错误1:忘记调用 loop() 导致消息不执行
// ❌ 错误
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
handler.post(() -> {
Log.d("TAG", "不会执行!");
});
// 忘记调用 Looper.loop(),消息永远不会处理
}).start();
// ✅ 正确
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
handler.post(() -> {
Log.d("TAG", "会执行");
});
Looper.loop(); // ★必须调用
}).start();
错误2:在 loop() 之后的代码永远不会执行
// ❌ 错误理解
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
Looper.loop(); // 阻塞在这里
Log.d("TAG", "永远不会执行!"); // ★除非 quit()
}).start();
错误3:主线程调用 quit() 导致应用崩溃
// ❌ 错误:尝试退出主线程 Looper
Looper.getMainLooper().quit(); // 抛出异常:"Main thread not allowed to quit."
错误4:多次调用 prepare()
// ❌ 错误
Looper.prepare();
Looper.prepare(); // 抛出异常:"Only one Looper may be created per thread"
3.3 性能优化建议
1. 避免在主线程创建子线程 Looper
// ❌ 不推荐:在主线程频繁创建销毁线程
for (int i = 0; i < 100; i++) {
new Thread(() -> {
Looper.prepare();
// ...
Looper.loop();
}).start();
}
// ✅ 推荐:复用 HandlerThread
HandlerThread thread = new HandlerThread("Worker");
thread.start();
Handler handler = new Handler(thread.getLooper());
// 多次使用同一个 Handler 发送任务
2. 使用 quitSafely() 而非 quit()
// ✅ 推荐:安全退出,等待当前消息处理完成
handlerThread.quitSafely();
// ❌ 不推荐:立即退出,可能丢失正在处理的消息
handlerThread.quit();
quitSafely() 实现原理:
// 【Android 12】frameworks/base/core/java/android/os/MessageQueue.java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
// ★安全模式:只移除未到执行时间的消息
removeAllFutureMessagesLocked();
} else {
// ★立即模式:移除所有消息
removeAllMessagesLocked();
}
nativeWake(mPtr);
}
}
四、面试真题解析
4.1 基础必答题(P5必须掌握)
【高频题1】主线程的 Looper 是何时创建的?为什么不需要手动调用 prepare()?
标准答案(30秒): 主线程的 Looper 在应用启动时由 ActivityThread.main() 方法自动创建。main 方法是应用进程的入口,在方法开头会调用 Looper.prepareMainLooper() 创建主线程 Looper,然后创建 ActivityThread 实例和主线程 Handler,最后调用 Looper.loop() 开启消息循环。因为系统已经自动完成了创建和启动,所以应用代码不需要也不应该手动调用 prepare() 和 loop(),如果调用反而会抛出异常。
深入展开(追问后):
ActivityThread.main() 源码流程:
public static void main(String[] args) {
// 1. 创建主线程 Looper
Looper.prepareMainLooper();
// 2. 创建 ActivityThread 实例
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// 3. 获取主线程 Handler(H)
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// 4. 开启消息循环
Looper.loop();
// 5. 如果循环退出,抛出异常
throw new RuntimeException("Main thread loop unexpectedly exited");
}
为什么 loop() 之后的代码也会执行(抛出异常):
Looper.loop()正常情况下是死循环,永远不会返回- 只有调用
quit()时才会退出循环,但主线程 Looper 不允许退出 - 如果 loop() 返回了,说明发生了严重错误,抛出异常让应用崩溃是合理的保护机制
面试官追问:
追问1:如果在 Application.onCreate() 中调用 Looper.prepare() 会怎样?
答:会抛出异常 "Only one Looper may be created per thread"。因为:
- Application.onCreate() 在主线程执行
- 此时 ActivityThread.main() 已经调用过 prepareMainLooper()
- 主线程已经有 Looper,再次调用 prepare() 违反单例原则
追问2:主线程的 Looper 和子线程的 Looper 创建有什么区别?
答:主要区别有三点:
| 对比项 | 主线程 Looper | 子线程 Looper |
|---|---|---|
| 创建方法 | prepareMainLooper() | prepare() |
| quitAllowed | false(不允许退出) | true(允许退出) |
| 调用者 | 系统自动调用 | 开发者手动调用 |
| 全局访问 | 可通过 getMainLooper() 获取 | 只能在创建线程内获取 |
| 生命周期 | 应用运行期间永不退出 | 可调用 quit() 退出 |
【高频题2】为什么一个线程只能有一个 Looper?如何保证的?
标准答案(30秒): 一个线程只能有一个 Looper 是通过 ThreadLocal 和检查机制保证的。Looper 使用静态的 ThreadLocal 变量 sThreadLocal 存储每个线程的 Looper 实例,prepare() 方法在创建 Looper 前会先检查 sThreadLocal.get() 是否为 null,如果不为 null 说明已经创建过,会抛出异常"Only one Looper may be created per thread"。这样设计的原因是一个线程只需要一个消息队列,多个队列会导致消息处理混乱且浪费资源。
深入展开(追问后):
保证机制源码:
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
// 静态 ThreadLocal,所有线程共享这个对象,但存储的值互相隔离
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
// ★检查点:如果当前线程已有 Looper,抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// ★创建 Looper 并存入当前线程的 ThreadLocal 槽位
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
// ★获取当前线程的 Looper
return sThreadLocal.get();
}
ThreadLocal 工作原理(简化说明,详细内容见 ./03-ThreadLocal机制.md):
Thread-1: ThreadLocalMap { sThreadLocal -> Looper1 }
Thread-2: ThreadLocalMap { sThreadLocal -> Looper2 }
Thread-3: ThreadLocalMap { sThreadLocal -> Looper3 }
sThreadLocal.get() 在不同线程返回不同的 Looper
面试官追问:
追问1:为什么不设计成一个线程可以有多个 Looper?
答:技术上可以实现(例如用 List 存储),但没有必要且有害:
- 语义混乱:多个消息队列如何决定消息进入哪个队列?
- 资源浪费:每个 Looper 都会创建 MessageQueue 和 Native 层对象(epoll 文件描述符)
- 性能问题:多个 loop() 循环会竞争 CPU,降低效率
- 设计原则:一个线程一个任务队列是经典的线程模型,简单清晰
追问2:如果真的需要在一个线程处理多个任务队列怎么办?
答:使用多个 Handler 共享一个 Looper:
Looper.prepare();
// 同一个线程,多个 Handler 处理不同类型的消息
Handler uiHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 处理 UI 相关消息
}
};
Handler networkHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 处理网络相关消息
}
};
Looper.loop(); // 所有消息都在同一个 MessageQueue 中按顺序处理
【高频题3】子线程中使用 Handler 为什么必须手动调用 prepare() 和 loop()?
标准答案(30秒): 子线程必须手动调用 prepare() 和 loop() 是因为 Android 不知道开发者是否需要在子线程中使用消息机制。主线程一定需要消息循环来处理生命周期回调和 UI 事件,所以系统自动创建,但子线程的用途是多样的,可能只是执行一次性任务就结束,也可能需要持续运行处理消息。如果不手动调用 prepare() 直接创建 Handler,会抛出异常"Can't create handler inside thread that has not called Looper.prepare()",这是一种显式声明机制,让开发者明确知道该线程会持续运行。
深入展开(追问后):
Handler 构造函数的检查:
// 【Android 12】frameworks/base/core/java/android/os/Handler.java
public Handler(@Nullable Callback callback, boolean async) {
// ...
// ★获取当前线程的 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
// ★如果为 null,说明未调用 prepare(),抛出异常
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 从 Looper 获取 MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
为什么不自动调用 prepare()?
假设系统自动调用,会带来以下问题:
// 假设系统自动调用 prepare()
new Thread(() -> {
Handler handler = new Handler(); // 系统自动 prepare()
handler.post(() -> {
Log.d("TAG", "任务");
});
// 线程结束,但 Looper 还在运行,导致线程无法退出!
// 必须显式调用 quit(),但开发者可能不知道
}).start();
显式调用的好处:
// 开发者明确知道需要消息循环
new Thread(() -> {
Looper.prepare(); // ★显式声明
Handler handler = new Handler();
handler.post(() -> {
// ...
});
Looper.loop(); // ★显式启动
// 开发者知道这里不会执行,除非调用 quit()
}).start();
面试官追问:
追问1:如果在子线程中只使用主线程的 Handler,需要调用 prepare() 吗?
答:不需要。例如:
Handler mainHandler = new Handler(Looper.getMainLooper());
new Thread(() -> {
// 不需要调用 Looper.prepare()
String result = loadData();
mainHandler.post(() -> {
textView.setText(result); // 在主线程执行
});
}).start();
原因:Handler 绑定的是主线程的 Looper,消息会发送到主线程的 MessageQueue,与当前线程是否有 Looper 无关。
追问2:HandlerThread 为什么不需要手动调用 prepare() 和 loop()?
答:HandlerThread 在 run() 方法中已经自动调用了:
// 【Android 12】frameworks/base/core/java/android/os/HandlerThread.java
public void run() {
mTid = Process.myTid();
Looper.prepare(); // ★自动调用
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop(); // ★自动调用
mTid = -1;
}
这就是 HandlerThread 的价值:封装了 Looper 的创建和启动细节,提供简洁的 API。
【高频题4】Looper.loop() 是死循环,为什么不会阻塞主线程导致 ANR?
标准答案(30秒): Looper.loop() 虽然是死循环,但不会导致 ANR,原因有三点:第一,主线程本来就应该持续运行处理事件,loop() 是正常的工作状态;第二,当消息队列为空时,loop() 会调用 nativePollOnce() 进入阻塞状态,通过 Linux 的 epoll 机制主动释放 CPU,不会空转占用资源;第三,ANR 是因为单个消息处理时间过长导致的,不是因为 loop() 循环本身。实际上如果 loop() 退出了,主线程无法处理任何事件,应用会直接崩溃。
深入展开(追问后):
loop() 内部的阻塞机制(详细内容见 ./02-Looper.loop()循环机制.md):
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
// ★阻塞式获取消息
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
// 分发消息
msg.target.dispatchMessage(msg);
// 回收消息
msg.recycleUnchecked();
}
}
MessageQueue.next() 的阻塞:
// 【Android 12】frameworks/base/core/java/android/os/MessageQueue.java
Message next() {
int nextPollTimeoutMillis = 0;
for (;;) {
// ★Native 层阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
Message msg = mMessages;
if (msg != null && now >= msg.when) {
return msg; // 返回消息
} else if (msg == null) {
nextPollTimeoutMillis = -1; // 无限阻塞
} else {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
}
}
}
}
阻塞时长:
| 场景 | nextPollTimeoutMillis | 行为 |
|---|---|---|
| 队列为空 | -1 | 无限阻塞,直到新消息到达 |
| 有延迟消息 | msg.when - now | 阻塞到消息执行时间 |
| 有即时消息 | 0 | 不阻塞,立即返回 |
epoll 机制(简化说明):
队列为空 → nativePollOnce(-1) → epoll_wait() → 线程进入休眠 → 不占用 CPU
新消息到达 → nativeWake() → 写入管道 → epoll_wait() 返回 → 线程唤醒
面试官追问:
追问1:如果主线程一直有消息处理,会导致 ANR 吗?
答:不一定,取决于单个消息的处理时间:
// ✅ 不会 ANR:1000 个消息,每个 10ms,总共 10 秒
for (int i = 0; i < 1000; i++) {
handler.post(() -> {
Thread.sleep(10); // 每个消息 10ms
});
}
// ❌ 会 ANR:1 个消息,处理 6 秒
handler.post(() -> {
Thread.sleep(6000); // 单个消息超过 5 秒 → ANR
});
ANR 的判断依据是输入事件 5 秒无响应,而非消息队列是否繁忙。
追问2:如果 loop() 退出会怎样?
答:主线程会崩溃:
public static void main(String[] args) {
Looper.prepareMainLooper();
// ...
Looper.loop();
// ★如果 loop() 返回,抛出异常
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主线程 Looper 退出意味着无法处理任何生命周期回调、UI 事件、系统消息,应用已经无法正常工作,崩溃是合理的保护机制。
【高频题5】如何获取主线程的 Looper?为什么任何线程都可以获取?
标准答案(30秒): 通过 Looper.getMainLooper() 静态方法可以获取主线程的 Looper,任何线程都可以调用。原因是主线程 Looper 在创建时会保存到 Looper 类的静态变量 sMainLooper 中,这个变量是全局共享的,通过 synchronized 保护线程安全。这样设计的目的是方便子线程向主线程发送消息更新 UI,只需要创建一个绑定主线程 Looper 的 Handler,就可以实现线程间通信。
深入展开(追问后):
源码实现:
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
// 主线程 Looper 的静态变量,全局唯一
private static Looper sMainLooper;
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// ★保存主线程 Looper 到静态变量
sMainLooper = myLooper();
}
}
/**
* 获取主线程 Looper(线程安全)
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
对比 myLooper():
| 方法 | 返回值 | 线程限制 | 用途 |
|---|---|---|---|
| myLooper() | 当前线程的 Looper | 只能获取当前线程的 | 在本线程创建 Handler |
| getMainLooper() | 主线程 Looper | 任何线程都可以获取 | 向主线程发送消息 |
典型应用场景:
// 子线程中向主线程发送消息
new Thread(() -> {
// 执行耗时操作
String result = loadDataFromNetwork();
// 创建主线程 Handler
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(() -> {
// 在主线程更新 UI
textView.setText(result);
});
}).start();
面试官追问:
追问1:getMainLooper() 为什么要用 synchronized 保护?
答:虽然 sMainLooper 只会赋值一次(在 prepareMainLooper 中),但并发读取仍然需要同步保证可见性:
// 线程 A(主线程)
sMainLooper = myLooper(); // 写入
// 线程 B(子线程)
Looper looper = sMainLooper; // 读取
如果没有 synchronized,线程 B 可能读取到未完全初始化的 Looper 对象(Java 内存模型的重排序问题)。synchronized 提供了内存屏障,确保可见性。
追问2:为什么不把所有线程的 Looper 都存到全局 Map 中?
答:没有必要,且有害:
- 隐私性:子线程的 Looper 是线程私有的,不应该被其他线程随意访问
- 生命周期:子线程可能随时退出,全局 Map 会持有已死亡线程的 Looper 引用,导致内存泄漏
- 主线程特殊性:只有主线程 Looper 需要全局访问(更新 UI),其他线程不需要
如果子线程需要接收其他线程的消息,应该主动暴露自己的 Handler:
public class WorkerThread extends Thread {
private Handler mHandler;
@Override
public void run() {
Looper.prepare();
mHandler = new Handler();
Looper.loop();
}
// 主动暴露 Handler
public Handler getHandler() {
return mHandler;
}
}
4.2 进阶加分题(P6/P6+)
【进阶题1】Looper、MessageQueue、Native Looper 的关系是什么?创建流程是怎样的?
参考答案:
Looper 的创建涉及 Java 层和 Native 层的双层结构:
三者关系:
Java 层:
Looper (Java对象)
↓ 包含
MessageQueue (Java对象)
↓ 持有指针 mPtr
Native 层:
NativeMessageQueue (C++对象)
↓ 包含
Native Looper (C++对象)
↓ 包含
epoll 文件描述符
完整创建流程:
// 1. Java 层创建 Looper
Looper.prepare()
→ new Looper(quitAllowed)
→ mQueue = new MessageQueue(quitAllowed) // 创建 MessageQueue
// 2. MessageQueue 创建时调用 Native 方法
MessageQueue(boolean quitAllowed)
→ mPtr = nativeInit() // JNI 调用
// 3. Native 层创建
android_os_MessageQueue_nativeInit()
→ NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue()
→ mLooper = Looper::getForThread()
→ if (mLooper == NULL)
mLooper = new Looper(false) // 创建 Native Looper
Looper::setForThread(mLooper)
→ return reinterpret_cast<jlong>(nativeMessageQueue) // 返回指针
Native Looper 的作用:
// 【Android 12】system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollFd(-1),
mEpollRebuildRequired(false),
mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
// ★创建 eventfd(用于唤醒)
mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
// ★创建 epoll 实例
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
// ★将 eventfd 加入 epoll 监听
struct epoll_event eventItem;
memset(&eventItem, 0, sizeof(epoll_event));
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, &eventItem);
}
追问:
1. 为什么需要 Native 层的 Looper?
答:主要原因有三点:
- epoll 机制:阻塞和唤醒需要使用 Linux 的 epoll 系统调用,Java 层无法直接操作
- Native 消息:系统的 Native 层也需要发送消息(例如输入事件、VSYNC 信号),需要 Native Looper 处理
- 跨进程通信:Binder 驱动写入数据后需要唤醒应用进程,通过 epoll 机制实现
2. Java Looper 和 Native Looper 是一一对应的吗?
答:不一定。Java Looper 一定会创建对应的 Native Looper,但 Native 层可以单独使用 Native Looper 而不创建 Java Looper(例如某些系统服务)。
【进阶题2】为什么主线程 Looper 不允许退出?如果强制退出会怎样?
参考答案:
主线程 Looper 不允许退出是通过 quitAllowed = false 参数控制的:
// 主线程创建
Looper.prepareMainLooper()
→ prepare(false) // ★传入 false
→ new Looper(false) // quitAllowed = false
// 退出时检查
MessageQueue.quit(boolean safe)
→ if (!mQuitAllowed)
throw new IllegalStateException("Main thread not allowed to quit.")
不允许退出的原因:
- 生命周期依赖:所有 Activity/Service/BroadcastReceiver 的生命周期回调都通过主线程 Looper 分发
- UI 更新依赖:所有 View 的绘制、事件分发都在主线程
- 系统消息依赖:系统服务(AMS、WMS)通过主线程 Looper 与应用通信
如果强制退出的后果:
假设可以退出,会导致:
// 假设主线程 Looper 退出
Looper.getMainLooper().quit();
// 后果1:ActivityThread.main() 抛出异常
Looper.loop(); // 返回
throw new RuntimeException("Main thread loop unexpectedly exited"); // 应用崩溃
// 后果2:即使不崩溃,也无法处理任何事件
// - 用户点击无响应
// - Activity 无法启动/暂停/销毁
// - UI 无法更新
// - 应用等同于"死亡"状态
追问:子线程的 Looper 可以退出,退出后会怎样?
答:子线程 Looper 退出后,loop() 方法返回,线程继续执行后续代码:
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
// 发送一个退出消息
handler.postDelayed(() -> {
Looper.myLooper().quit(); // 退出 Looper
}, 5000);
Looper.loop(); // 阻塞 5 秒后返回
Log.d("TAG", "Looper 退出,线程继续执行");
// 可以执行清理工作
}).start();
退出后的影响:
handler.post()发送的消息不会再处理MessageQueue.next()返回 null,loop() 退出- 线程可以正常结束或继续执行其他代码
【进阶题3】ThreadLocal 在 Looper 中的作用是什么?为什么不用 static Map<Thread, Looper>?
参考答案:
ThreadLocal 在 Looper 中的作用是实现每个线程独立存储自己的 Looper,避免多线程竞争:
// 【Android 12】frameworks/base/core/java/android/os/Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); // 获取当前线程的 Looper
}
ThreadLocal 原理(简化):
// ThreadLocal 内部机制
class ThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = t.threadLocals; // 每个 Thread 都有自己的 Map
if (map != null) {
return map.get(this); // 以 ThreadLocal 为 key 获取值
}
return null;
}
}
// 每个 Thread 对象内部
class Thread {
ThreadLocal.ThreadLocalMap threadLocals; // 存储线程私有数据
}
为什么不用 static Map<Thread, Looper>?
| 对比项 | ThreadLocal | static Map<Thread, Looper> |
|---|---|---|
| 性能 | O(1) 直接从线程对象获取 | O(1) 但需要锁保护 |
| 线程安全 | 无需同步(线程私有) | 必须 synchronized |
| 内存泄漏风险 | Thread 结束自动清理 | 需要手动移除,容易泄漏 |
| 语义清晰 | "线程本地变量"语义明确 | 需要额外管理 Map |
static Map 的问题示例:
// 假设使用 static Map
static Map<Thread, Looper> sLooperMap = new HashMap<>();
private static void prepare(boolean quitAllowed) {
Thread thread = Thread.currentThread();
synchronized (sLooperMap) { // ★必须加锁
if (sLooperMap.containsKey(thread)) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sLooperMap.put(thread, new Looper(quitAllowed));
}
}
// ★问题:线程结束后,Map 中的 Entry 不会自动移除
// Thread 对象无法被 GC 回收 → 内存泄漏
// 需要手动清理(容易忘记)
public void quit() {
mQueue.quit();
synchronized (sLooperMap) {
sLooperMap.remove(Thread.currentThread()); // 手动移除
}
}
ThreadLocal 的自动清理:
// Thread 结束时,Thread 对象被回收
// Thread.threadLocals(ThreadLocalMap)也被回收
// 其中存储的 Looper 对象自动被 GC 回收
// 无需手动管理
追问:ThreadLocal 一定不会内存泄漏吗?
答:不一定。如果 Thread 对象长期存活(例如线程池中的线程),ThreadLocal 可能导致泄漏:
// 线程池场景
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(() -> {
Looper.prepare(); // ThreadLocal 存储 Looper
// ...
// ❌ 忘记调用 quit() 和清理 ThreadLocal
});
// 线程池的线程不会销毁,Thread.threadLocals 持有 Looper
// Looper 持有 MessageQueue、Native 对象 → 内存泄漏
正确做法:
executor.execute(() -> {
try {
Looper.prepare();
// ...
Looper.loop();
} finally {
// ★清理 ThreadLocal(Looper 内部会自动做)
Looper.myLooper().quit();
}
});
详细的 ThreadLocal 内存泄漏问题分析见 ./03-ThreadLocal机制.md。
4.3 实战场景题
【场景题】在子线程中创建了 Handler,但消息一直不执行,如何排查?
场景描述:
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
handler.post(() -> {
Log.d("TAG", "消息不执行!");
});
// ...其他代码
}).start();
问题分析:
- 根本原因:忘记调用
Looper.loop(),消息循环未启动 - 表现:消息已插入 MessageQueue,但没有循环去取出处理
- 线程状态:线程执行完 run() 方法后直接结束
排查步骤:
步骤1:检查 Looper 是否创建
Looper looper = Looper.myLooper();
if (looper == null) {
Log.e("TAG", "未调用 Looper.prepare()");
} else {
Log.d("TAG", "Looper 已创建");
}
步骤2:检查是否调用 loop()
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
handler.post(() -> {
Log.d("TAG", "消息");
});
Log.d("TAG", "loop() 之前");
Looper.loop(); // ★必须调用
Log.d("TAG", "loop() 之后"); // 不会执行(除非 quit)
}).start();
步骤3:检查消息队列
// 通过反射查看消息队列(调试用)
MessageQueue queue = Looper.myLooper().getQueue();
// 检查是否有消息
解决方案:
方案1:手动管理(不推荐)
new Thread(() -> {
Looper.prepare();
Handler handler = new Handler();
handler.post(() -> {
Log.d("TAG", "消息执行了");
});
Looper.loop(); // ★添加此行
}).start();
方案2:使用 HandlerThread(推荐)
HandlerThread thread = new HandlerThread("WorkThread");
thread.start(); // 内部自动 prepare() 和 loop()
Handler handler = new Handler(thread.getLooper());
handler.post(() -> {
Log.d("TAG", "消息执行了");
});
// 使用完毕后退出
thread.quitSafely();
追问:
1. 如果调用了 loop() 但消息仍不执行,可能是什么原因?
答:可能的原因:
- Handler 创建线程不对:
Handler mainHandler = new Handler(Looper.getMainLooper());
new Thread(() -> {
Looper.prepare();
mainHandler.post(() -> {
// 这个会在主线程执行,不是当前子线程
});
Looper.loop(); // 当前线程的循环没有消息
}).start();
- 消息被移除:
handler.post(runnable);
handler.removeCallbacks(runnable); // 消息被移除
- Looper 已退出:
Looper.prepare();
Handler handler = new Handler();
Looper.myLooper().quit(); // 提前退出
handler.post(() -> {
// 不会执行
});
Looper.loop(); // 立即返回
2. 如何监控子线程的消息处理情况?
答:使用 MessageLogging:
Looper.myLooper().setMessageLogging(new Printer() {
@Override
public void println(String x) {
if (x.startsWith(">>>>>")) {
Log.d("Looper", "开始处理: " + x);
} else if (x.startsWith("<<<<<")) {
Log.d("Looper", "处理完成: " + x);
}
}
});
五、对比与总结
5.1 关键方法对比
| 方法 | 作用 | 调用线程 | 使用场景 | 注意事项 |
|---|---|---|---|---|
| prepare() | 创建子线程 Looper | 需要消息循环的子线程 | 手动管理消息线程 | 必须在创建 Handler 前调用 |
| prepareMainLooper() | 创建主线程 Looper | 主线程(系统调用) | 应用启动时 | 应用代码不应该调用 |
| loop() | 启动消息循环 | 已调用 prepare() 的线程 | prepare() 之后 | 会阻塞,直到 quit() |
| myLooper() | 获取当前线程 Looper | 任何线程 | 创建 Handler、检查 Looper | 可能返回 null |
| getMainLooper() | 获取主线程 Looper | 任何线程 | 向主线程发消息 | 全局唯一,线程安全 |
| quit() | 退出消息循环 | Looper 所在线程 | 线程结束前清理 | 主线程 Looper 不允许 |
| quitSafely() | 安全退出(处理完当前消息) | Looper 所在线程 | 优雅关闭线程 | Android 4.1+ |
5.2 核心要点速记
一句话记忆: Looper 通过 ThreadLocal 实现每个线程独立存储,主线程由系统自动创建且不允许退出,子线程需手动调用 prepare() 创建和 loop() 启动,创建时会自动生成 MessageQueue 和 Native 层对应的 Looper。
3个关键点:
- 线程隔离:ThreadLocal 机制确保每个线程只有一个 Looper
- 主线程特殊:自动创建、不允许退出、全局可访问
- 双层结构:Java Looper + Native Looper,支持 Java 和 Native 消息
面试官最爱问:
- 主线程 Looper 何时创建?为什么不需要手动调用?
- 为什么一个线程只能有一个 Looper?如何保证?
- 子线程为什么必须手动调用 prepare() 和 loop()?
- Looper.loop() 是死循环为什么不会 ANR?
- 如何获取主线程 Looper?为什么任何线程都可以获取?
六、关联知识点
前置知识:
- Handler 消息机制概述(详见:
../01-Handler基础/01-Handler基本概念.md) - Java 线程基础(Thread、Runnable)
后续扩展:
- Looper.loop() 循环机制详解(详见:
./02-Looper.loop()循环机制.md) - ThreadLocal 实现原理与内存泄漏(详见:
./03-ThreadLocal机制.md) - MessageQueue 消息队列原理
- epoll 阻塞唤醒机制
相关文件:
./02-Looper.loop()循环机制.md- loop() 方法内部的消息处理流程./03-ThreadLocal机制.md- ThreadLocal 工作原理、内存泄漏问题../01-Handler基础/03-Handler工作流程.md- Handler 完整工作流程