0 背景
在操作系统中,消息队列处理框架是操作系统为应用开发者提供的最基本的功能。

在 Android 中,消息队列通过 Looper / Message / MessageHandler 等native 对象提供,并通过 JNI 封装了 Java API 供上层应用使用。
本文重点分析 Looper.cpp native 相关代码,从底层分析 Android 的消息队列处理框架。
bash
/system/core/libutils/Looper.cpp
/system/core/libutils/Looper.h
1 数据模型
消息队列需要包含如下模型:消息,循环队列,消息处理回调: Native Looper 中的各个模块关系如下:

1.1 Message 消息:
仅包含一个int 字段,what,表示:这是一个什么消息
scss
/**
* A message that can be posted to a Looper.
*/
struct Message {
Message() : what(0) { }
Message(int w) : what(w) { }
/* The message type. (interpretation is left up to the handler) */
int what;
};
1.2 MessageHandler 消息处理器:
收到消息如何处理,所以仅仅定义了一个 virtual 方法:handleMessage
arduino
/**
* Interface for a Looper message handler.
*
* The Looper holds a strong reference to the message handler whenever it has
* a message to deliver to it. Make sure to call Looper::removeMessages
* to remove any pending messages destined for the handler so that the handler
* can be destroyed.
*/
class MessageHandler : public virtual RefBase {
protected:
virtual ~MessageHandler();
public:
/**
* Handles a message.
*/
virtual void handleMessage(const Message& message) = 0;
};
1.3 Looper::MessageEnvelope:
定义在 Looper 类内部的一个私有类,把 Message 和 MessageHandler 关联在了一起,并记录上消息的处理时间
scss
struct MessageEnvelope {
MessageEnvelope() : uptime(0) { }
MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)
: uptime(u), handler(std::move(h)), message(m) {}
nsecs_t uptime;
sp<MessageHandler> handler;
Message message;
};
1.4 Looper::mMessageEnvelopes:
真正的 "消息队列",用 Vector 保存 Looper 收到的所有信息,在 Looper 被唤醒以后,依次处理
kotlin
class Looper {
private:
Vector<MessageEnvelope> mMessageEnvelopes
}
1.5 Looper
封装了所有"消息队列" 相关的API 和数据结构,重要 API 如下:
arduino
class Looper {
public:
// Looper 的构造函数,线程唯一
static sp<Looper> prepare(int opts);//
static void setForThread(const sp<Looper>& looper);
static sp<Looper> getForThread();
// 等待消息的到来,处理消息
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
// 唤醒 Looper 线程,pollOnce 的 epoll_wait 收到信号,开始执行处理流程
void wake();
// 发送消息的 API, 支持定时发送和延迟发送
void sendMessage(const sp<MessageHandler>& handler, const Message& message);
void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
const Message& message);
void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message);
// 取消发送的消息
void removeMessages(const sp<MessageHandler>& handler);
void removeMessages(const sp<MessageHandler>& handler, int what);
}
从上面的代码片段可知,Looper 的 API 包含4大类:
1 Looper::prepare 为当前线程创建 Looper 对象
2 pollOnce 开始通过 epoll_wait 持续等待消息, 如果等到消息后,调用对应 Message 的 MessageHandler.handleMessage 进行处理
3/4 sendMessage removeMessage 是 Looper 消息队列的发送和取消接口
2 Looper::prepare 流程
Looper::prepare 为当前的线程建立 Looper 对象,简化后的 prepare 如下:
step 1: Looper::getForThread() 查看当前线程是否已经有 Looper,
step 2: 如果没有,new 一个 Looper 并保存到当前线程
ini
sp<Looper> Looper::prepare(int opts) {
// 第一次调用,生成一个 TLS key,并根据 key 查询是否有对应线程的 Looper
sp<Looper> looper = Looper::getForThread();
// 如果没有,说明是第一次调用,需要初始化 Looper
if (looper == nullptr) {
looper = new Looper();
// 保存 Looper 对象到 TLS 中
Looper::setForThread(looper);
}
return looper;
}
2.1 Looper 的线程唯一特性
TLS 可以理解为以线程为 key 的 HashMap,每个线程保存一个唯一的 Looper 对象。具体实现,依赖的是 pthread 提供的 thread local 特性:
pthread_setspecific
和pthread_getspecific
: 这两个函数用于在特定线程中设置和获取线程局部存储(TLS)键的值。这样,每个线程都可以有一个独立的Looper对象。
scss
pthread_setspecific(gTLSKey, looper.get());
pthread_getspecific(gTLSKey);
其中,使用 pthread_key_create 生成一个唯一的 key
pthread_key_create
pthread_once 保证 initTLSKey 仅生成一次
scss
pthread_once(& gTLSOnce, initTLSKey);
scss
void Looper::initTLSKey() {
int error = pthread_key_create(&gTLSKey, threadDestructor);
}
void Looper::threadDestructor(void *st) {
Looper* const self = static_cast<Looper*>(st);
if (self != nullptr) {
self->decStrong((void*)threadDestructor);
}
}
void Looper::setForThread(const sp<Looper>& looper) {
sp<Looper> old = getForThread(); // also has side-effect of initializing TLS
if (looper != nullptr) {
looper->incStrong((void*)threadDestructor);
}
pthread_setspecific(gTLSKey, looper.get());
if (old != nullptr) {
old->decStrong((void*)threadDestructor);
}
}
sp<Looper> Looper::getForThread() {
int result = pthread_once(& gTLSOnce, initTLSKey);
return (Looper*)pthread_getspecific(gTLSKey);
}
2.2 Looper 建立 epoll 相关的事件监听
Looper 对象创建时,初始化了 epoll 相关功能:
scss
Looper::Looper() {
// mWakeEventFd 用于唤醒 Looper 线程中的 epoll_wait
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
// 建立 epoll_wait
rebuildEpollLocked();
}
其中:rebuildEpollLocked 建立 epoll 事件监听:
第一次 Looper::prepare 时,新建 epoll 对象,仅仅监听一个 fd -- mWakeEventFd
ini
void Looper::rebuildEpollLocked() {
// Allocate the new epoll instance and register the wake pipe.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd.get();
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
}
后续可以通过 addFd 添加更多 fd 的监听,Android 的 Message / Handler / Looper 机制,仅使用了 mWakeEveentFd 这一个 fd
3 Looper 发送消息流程
3.1 Looper::sendMessage
向消息队列发送一个消息,使用 sendMessage 系列 API,有三个参数:
a. nsecs_t uptime: 消息的分发时间,Looper 中的消息队列,按照时间顺序排列
b. const sp& handler, 消息处理函数,Looper 调用对应消息的 handleMessage 方法处理对性消息
c. const Message& message, 消息本身,只有一个 what 参数
发送具体过程可以分为三步:详见下面注释:
arduino
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
const Message& message) {
// Step 1: 按照发送时间,找到消息需要插入的位置
size_t i = 0;
size_t messageCount = mMessageEnvelopes.size();
while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
i += 1;
}
// Step 2: 消息封装为 MessageEnvelope,插入到 vector mMessageEnvelopes
MessageEnvelope messageEnvelope(uptime, handler, message);
mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
// Step 3: 如果插入到了第一个位置,唤醒 Looper 线程进行处理
// Wake the poll loop only when we enqueue a new message at the head.
if (i == 0) {
wake();
}
}
3.1 Looper::wake
其中, wake() 方法使用了构造函数中新建的 fd: mWakeEventFd
c
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
也就是通过 write() 系统调用,给 mWakeEventFd 写入了一个 1
由于在 Looper 创建时, epoll 对象在监听 mWakeEventFd 的变化。因此,当写入 1 时,epoll_wait 函数就会收到对应的事件,被唤醒。
4 Looper 处理消息流程
4.1 Looper::pollOnce
在 Looper::prepare 创建 Looper 对象后,调用方就会执行 Looper::pollOnce 启动消息循环。
arduino
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
...... // 处理 Response 相关事件的逻辑暂不分析
result = pollInner(timeoutMillis);
}
}
从上面代码中,可以看到这是一个死循环。从这里也诞生了一个著名的面试问题:
Looper 一直在主线程中死循环,为什么不会造成 ANR
4.2 Looper::pollInner
我们进入 pollInner 中分析一下消息处理逻辑:
第一部分逻辑是如下: epoll_wait 部分,在 Looper::prepare 中,我们建立了对 mWakeEventFd 的监听。
一旦客户端有调用 sendMessage 相关逻辑,epoll_wait 就会返回,否则,线程卡死在 epoll_wait 过程。
ini
int Looper::pollInner(int timeoutMillis) {
// We are about to idle.
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mPolling = false;
......
}
例如:在ANR 的 trace 日志中,我们经常可以看到,有大量线程处于 nativePollOnce 状态,也就是等待事件到达。
less
"main" prio=5 tid=1 Native
| group="main" sCount=1 ucsCount=0 flags=1 obj=0x73145898 self=0xb400006f3b6e2be0
| sysTid=1866 nice=0 cgrp=foreground sched=0/0 handle=0x70902df4f8
| state=S schedstat=( 918826499730 201286858402 3920982 ) utm=56932 stm=34949 core=3 HZ=100
| stack=0x7fc6fc2000-0x7fc6fc4000 stackSize=8188KB
| held mutexes=
native: #00 pc 00000000000ad7a8 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 45cceace7cf5c8c8a5a9c2064fa4532f)
native: #01 pc 0000000000017ebc /system/lib64/libutils.so (android::Looper::pollInner(int)+188) (BuildId: 10aac5d4a671e4110bc00c9b69d83d8a)
native: #02 pc 0000000000017da0 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112) (BuildId: 10aac5d4a671e4110bc00c9b69d83d8a)
native: #03 pc 000000000016058c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) (BuildId: 92a456bea4f19ed89af37ce405ef942c)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:335)
at android.os.Looper.loopOnce(Looper.java:167)
at android.os.Looper.loop(Looper.java:283)
at android.app.ActivityThread.main(ActivityThread.java:8186)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:573)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1015)
第二部分逻辑是,epoll_wait 收到事件后,如果是mWakeEventFd 的事件,直接调用 awoken()清理状态,表示事件已经收到。
ini
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd.get()) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
}
}
这里可以看到,Looper 可以监控多个 fd 的事件,最多可以监控 16个 (EPOLL_MAX_EVENTS = 16)。
第三部分逻辑:
从 mMessageEnvelopes 消息队列中,依次取出消息,如果消息事件已经超时,调用 MessageHandler 对应的 handleMessage 方法,处理消息。
ini
// Invoke pending message callbacks.
mNextMessageUptime = LLONG_MAX;
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
// Remove the envelope from the list.
// We keep a strong reference to the handler until the call to handleMessage
// finishes. Then we drop it so that the handler can be deleted *before*
// we reacquire our lock.
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
handler->handleMessage(message);
} // release handler
mLock.lock();
mSendingMessage = false;
result = POLL_CALLBACK;
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
4.3 Q:为什么 Looper 一直在主线程中死循环确不会ANR
为什么 Looper 一直在主线程中死循环,为什么不会造成 ANR?
1 从上面的分析中可以看到,主线程一直处于 epoll_wait 等待消息的状态,并不会消耗 CPU 资源。
2 ANR 可以认为是 system_server 监控客户端进程是否响应的看门狗机制,nativePollOnce 不会触发任何一种类型的 ANR Timer 超时。
为什么我们在 onCreate 或者 onResume 中死循环,会造成 ANR:
因为, AMS 在发送 scheduleCreate 或者 scheduleResume 事件时,启动了 ANR 定时器,客户端主线程卡住会造成定时器超时。
5 总结
本文分析了Android 最基础的 Looper Message MessageHandler 机制,这是 Android 应用框架和操作系统的基础。
通过对 C++ 代码的分析,我们可以对上层 Java 代码相关API的功能理解的更为深刻。
Looper 中的 epoll 除了可以监控默认的 mWakeEventFd 还可以最多监控 16 个其他的 fd。限于篇幅,本文就不再继续分析。