Looper 创建与启动

3 阅读30分钟

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()     // 内部自动调用

关键设计

  1. 线程隔离:通过 ThreadLocal 实现每个线程独立的 Looper
  2. 单例保证:同一线程多次调用 prepare() 会抛出异常
  3. 主线程特殊处理:主线程 Looper 不允许退出(quitAllowed = false)
  4. 自动创建 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");
}

关键点

  1. 自动创建:开发者无需手动调用,系统自动完成
  2. 创建时机:在 ActivityThread 实例化之前,确保后续代码可以使用主线程 Handler
  3. 异常保护: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;
    }
}

关键设计

  1. 单例保护:通过 synchronized 和 sMainLooper 检查确保只创建一次
  2. 全局访问:任何线程都可以通过 Looper.getMainLooper() 获取主线程 Looper
  3. 不可退出:传入 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();
}

关键点

  1. ThreadLocal 机制:每个线程独立存储自己的 Looper,互不干扰(详细原理见 ./03-ThreadLocal机制.md
  2. 重复检查:如果线程已有 Looper,抛出异常防止覆盖
  3. 可配置退出:通过 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;
}

关键设计

  1. 自动创建 MessageQueue:Looper 和 MessageQueue 一一对应,构造时自动创建
  2. 记录线程信息:保存创建 Looper 的线程引用,用于检查调用是否合法
  3. 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);
    }
}

关键点

  1. Java + Native 双层结构:Java 层 MessageQueue 对应 Native 层 NativeMessageQueue
  2. Native Looper:Native 层也有独立的 Looper,用于处理 Native 消息和 epoll 机制
  3. 跨语言通信: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;  // 队列退出,结束循环
        }
    }
}

关键点

  1. 前置检查:确保当前线程已经调用 prepare() 创建了 Looper
  2. 无限循环for(;;) 持续运行,除非队列退出
  3. 循环细节:具体的消息处理逻辑在 ./02-Looper.loop()循环机制.md 中详细讲解
2.3.2 启动时机对比
场景prepare() 调用位置loop() 调用位置谁负责调用
主线程ActivityThread.main() 开头ActivityThread.main() 结尾系统自动
子线程手动线程 run() 方法开头创建 Handler 后开发者手动
HandlerThreadHandlerThread.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);
    }
}

原因分析

  1. 主线程依赖:所有 Activity 生命周期、UI 更新、输入事件都通过主线程 Looper 处理
  2. 退出后果:主线程 Looper 退出会导致应用无法响应任何事件,等同于应用崩溃
  3. 保护机制:系统通过 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));
}

原因

  1. 设计原则:一个线程只需要一个消息队列,多个 MessageQueue 无意义且容易混乱
  2. ThreadLocal 限制:ThreadLocal 存储的是单个对象,不支持多个 Looper
  3. 避免资源浪费:每个 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);

注意事项

  1. Handler 获取时机:必须等待 run() 执行到 Handler 创建后才能获取,否则为 null
  2. 线程生命周期:loop() 会阻塞线程,直到调用 quit() 才结束
  3. 资源释放:不再使用时调用 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;
    }
}

优势

  1. 自动准备:无需手动调用 prepare() 和 loop()
  2. 安全获取:getLooper() 内部等待机制确保 Looper 创建完成
  3. 生命周期管理:提供 quit() 和 quitSafely() 方法
场景3:在任意线程向主线程发送消息

需求:子线程完成任务后更新 UI

实现方式

// 获取主线程 Looper 创建 Handler
Handler mainHandler = new Handler(Looper.getMainLooper());

// 在子线程中使用
new Thread(() -> {
    // 执行耗时操作
    String result = loadDataFromNetwork();

    // 切换到主线程更新 UI
    mainHandler.post(() -> {
        textView.setText(result);  // 在主线程执行
    });
}).start();

关键点

  1. Looper.getMainLooper():全局方法,任何线程都可以调用
  2. 线程切换:消息在 Handler 绑定的 Looper 所在线程执行(主线程)
  3. 无需准备:主线程 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"。因为:

  1. Application.onCreate() 在主线程执行
  2. 此时 ActivityThread.main() 已经调用过 prepareMainLooper()
  3. 主线程已经有 Looper,再次调用 prepare() 违反单例原则

追问2:主线程的 Looper 和子线程的 Looper 创建有什么区别?

答:主要区别有三点:

对比项主线程 Looper子线程 Looper
创建方法prepareMainLooper()prepare()
quitAllowedfalse(不允许退出)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 存储),但没有必要且有害:

  1. 语义混乱:多个消息队列如何决定消息进入哪个队列?
  2. 资源浪费:每个 Looper 都会创建 MessageQueue 和 Native 层对象(epoll 文件描述符)
  3. 性能问题:多个 loop() 循环会竞争 CPU,降低效率
  4. 设计原则:一个线程一个任务队列是经典的线程模型,简单清晰

追问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 中?

答:没有必要,且有害:

  1. 隐私性:子线程的 Looper 是线程私有的,不应该被其他线程随意访问
  2. 生命周期:子线程可能随时退出,全局 Map 会持有已死亡线程的 Looper 引用,导致内存泄漏
  3. 主线程特殊性:只有主线程 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?

答:主要原因有三点:

  1. epoll 机制:阻塞和唤醒需要使用 Linux 的 epoll 系统调用,Java 层无法直接操作
  2. Native 消息:系统的 Native 层也需要发送消息(例如输入事件、VSYNC 信号),需要 Native Looper 处理
  3. 跨进程通信: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)  // ★传入 falsenew Looper(false)  // quitAllowed = false

// 退出时检查
MessageQueue.quit(boolean safe)
  → if (!mQuitAllowed)
       throw new IllegalStateException("Main thread not allowed to quit.")

不允许退出的原因

  1. 生命周期依赖:所有 Activity/Service/BroadcastReceiver 的生命周期回调都通过主线程 Looper 分发
  2. UI 更新依赖:所有 View 的绘制、事件分发都在主线程
  3. 系统消息依赖:系统服务(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>?

对比项ThreadLocalstatic 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();

问题分析

  1. 根本原因:忘记调用 Looper.loop(),消息循环未启动
  2. 表现:消息已插入 MessageQueue,但没有循环去取出处理
  3. 线程状态:线程执行完 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() 但消息仍不执行,可能是什么原因?

答:可能的原因:

  1. Handler 创建线程不对
Handler mainHandler = new Handler(Looper.getMainLooper());

new Thread(() -> {
    Looper.prepare();
    mainHandler.post(() -> {
        // 这个会在主线程执行,不是当前子线程
    });
    Looper.loop();  // 当前线程的循环没有消息
}).start();
  1. 消息被移除
handler.post(runnable);
handler.removeCallbacks(runnable);  // 消息被移除
  1. 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个关键点

  1. 线程隔离:ThreadLocal 机制确保每个线程只有一个 Looper
  2. 主线程特殊:自动创建、不允许退出、全局可访问
  3. 双层结构:Java Looper + Native Looper,支持 Java 和 Native 消息

面试官最爱问

  1. 主线程 Looper 何时创建?为什么不需要手动调用?
  2. 为什么一个线程只能有一个 Looper?如何保证?
  3. 子线程为什么必须手动调用 prepare() 和 loop()?
  4. Looper.loop() 是死循环为什么不会 ANR?
  5. 如何获取主线程 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 完整工作流程