消息机制
Android 的消息机制基于 Handler、Looper、MessageQueue 实现,用于同一进程内的线程间通信。其核心目的是将任务切换到指定线程执行(如子线程更新 UI)。下面是一个简单的入门案例:
// 主线程创建 Handler(关联主线程 Looper)
val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// 处理消息(例如更新 UI)
if (msg.what == 1) {
Log.d("my_tag", "${Thread.currentThread().name}, $msg")
}
}
}
Thread {
// 子线程发送消息到主线程
val message = Message.obtain()
message.what = 1
handler.sendMessage(message)
}.start()
Android 消息机制中的三个核心组件如下:
Handler:消息的发送者和接收者;
Looper:消息循环器,负责从消息队列中取出消息,并分发给对应的 Handler;
MessageQueue:消息队列,用于存储待处理的消息。
Looper 消息循环器
每个线程可以通过 Looper.prepare()
来初始化自己的消息循环器。主线程在应用启动时就已经创建好了 Looper:
public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal {
public static void main(String[] args) {
// ...
Looper.prepareMainLooper(); // 为主线程准备消息循环
// ...
Looper.loop(); // 主线程开始处理消息(进入死循环)
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
Looper 的创建通过 ThreadLocal
实现,保证每个线程只存在一个 Looper 实例,每个 Looper 内部持有一个唯一的 MessageQueue
,用于处理该线程内所有的消息。
public final class Looper {
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
// 通过 ThreadLocal 确保一个线程只能存在一个 Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
// Looper 构造函数,每个 Looper 持有一个唯一的 MessageQueue
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// 获取当前线程中的 Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
Handler 发送消息
Handler 在构造时会绑定指定线程的 Looper(默认是当前线程的 Looper),并持有其内部的 MessageQueue。调用 sendMessage()
方法时,消息被放入该线程的队列中。
public class Handler {
// 构造函数
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async, boolean shared) {
mLooper = looper;
mQueue = looper.mQueue; // 获取 looper 中的消息队列
mCallback = callback;
mAsynchronous = async;
mIsShared = shared;
}
// 消息的发送本质是通过 enqueueMessage() 完成的:
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
msg.target = this; // 设置消息的目标为当前 Handler
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}
Looper 分发消息
Looper 创建后还需要开启消息循环,不然不能处理消息。主线程的 Looper 会在程序启动时就开启消息循环,它是一个死循环,会不断从 MessageQueue
中取出消息,并调用 msg.target.dispatchMessage()
进行分发:
public final class Looper {
public static void loop() {
final Looper me = myLooper();
// ....
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); // 分发消息
// ...
} catch (Exception exception) {
// ...
} finally {
// ...
}
}
}
消息在被 Handler 发送时,将 msg.target
属性设置为了自己 this
,当 Looper 拿到一条消息后,会调用 msg.target
(也就是发送时的 Handler)去处理消息,这样就实现了分发到指定的 Handler。
在 dispatchMessage()
内部,会调用我们覆写的 handleMessage()
方法:
public class Handler {
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
}
消息机制总结
初始化:线程的 Looper 必须先初始化和开启消息循环,主线程的 Looper 启动时已经创建好;
绑定:Handler 绑定到某个线程的 Looper,间接持有该线程的消息队列;
发送:Handler 发送消息,写入目标线程的消息队列;
分发:Looper 从消息队列中获取消息,调用消息中的
target
(即原始发送的 Handler),执行handleMessage()
处理逻辑。