本文基于安卓11。
Looper是一个用具,在安卓程序中,UI线程是由事件驱动的(onCreate, onResume, onDestory),Looper就是处理这些事件的工具,事件被描述为消息(Message),Looper维护一个按时间顺序排列的消息队列(MessageQueue),在要求的时间将消息交给Hanlder处理。
一、消息循环
默认情况下一个线程不存在消息循环,Android线程开启消息循环需要调用Looper.prepare(),Looper.loop()。
二、Looper.prepare()
初始化Looper和MessageQueue对象,存在应用层的Java对象和Native层的cpp对象。
//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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到Looper对象通过ThreadLocal获取,被设计为线程单例。
//MessageQueue.java
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue构造方法通过nativeInit()初始化native层的消息循环,创建native层的Looper,NativeMessageQueue对象,mPtr是NativeMessageQueue对象引用,后续MessageQueue(Java)调用nativePollOnce(long ptr, int timeoutMillis), nativeWake(long ptr)等native函数都需要传入这个参数,NativeMessageQueue可使用此参数通过JNIEnv*调用Java方法。
2.1 nativeInit()
//android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(NativeMessageQueue);
}
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
mPollEnv,mPollObj都置为NULL,获取当前线程Looper,返回NativeMessageQueue对象引用给Java层。
//Looper.cpp
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked();
}
重置mWakeEventFd,eventfd系统调用创建一个匿名文件描述符,rebuildEpollLocked()重置epoll事件。
//Looper.cpp
void Looper::rebuildEpollLocked() {
// Close old epoll instance if we have one.
if (mEpollFd >= 0) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ rebuildEpollLocked - rebuilding epoll set", this);
#endif
mEpollFd.reset();
}
// Allocate the new epoll instance and register the WakeEventFd.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
strerror(errno));
for (const auto& [seq, request] : mRequests) {
epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
epoll_create1()创建新的epoll实例,将唤醒事件mWakeEventFd添加到epoll实例,后面可以看到调用Looper::wake()方法在mWakeEventFd写值唤醒epoll_wait()。
三、Looper.loop()
//Looper.java
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);
//...
}
//...
msg.recycleUnchecked();
return true;
}
mQueue为Looper类的MessageQueue对象,loop方法很简单,循环获取处理mQueue的Message。
如果msgnull时返回false,loop退出消息循环,这种情况只会在mPtr0(nativeInit初始化失败),或者Lopper.quit()时才会出现,messageQueue处于空闲时会一直阻塞等待笑一个msg,而不会返回null。
msg.target是Handler对象,成功获取到msg后就会调用到handler的dispatchMessage(msg)---->handleMessage(msg)方法了。
msg.recycleUnchecked()对创建的Message对象进行缓存回收,避免创建过多的Message对象。
3.1 MessageQueue.next()
//MessageQueue.java
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
重点关注nextPollTimeoutMillis,mBlocked两个变量:
- nextPollTimeoutMillis:epoll阻塞时间。
- mBlocked:当前线程是否处于阻塞状态。
当没有需要处理的消息时,msg==null,nextPollTimeoutMillis值为-1,否则为下个需要处理消息的时间。
3.1.1 返回msg,交给handler处理
next()维护一个按时间排序的Message队列,如果有需要处理的消息,msg不为空(msg != null),且当前时间小于msg.when(now < msg.when),则返回当前的msg,交给handler处理。
3.1.2 处理IdleHandler
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
mIdlehandler变量保存着IdleHandler对象,当暂时没有需要处理的消息时,检查mIdleHandlers.size()是否为0,是否有需要处理的IdleHandler,利用空闲时间处理,可以看出IdleHandler处理时间不可控。关于IdleHandler(https://blog.csdn.net/jdsjlzx/article/details/110532500)
3.1.3 nativePollOnce
没有消息需要处理,也没有IdleHandler需要处理,nativePollOnce(ptr, nextPollTimeoutMillis);使当前线程阻塞,进入休眠状态。
参数timeoutMillis就是Java应用层传递过来的nextPollTimeoutMillis变量。
//Looper.cpp
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;
//...
return result;
}
epoll_wait()系统调用等待文件描述符mEpollFd引用的epoll实例上的事件,从此线程进入休眠状态,直到timeoutMillis时间结束,或者接收到新的事件。
四、发送消息
Handler通过Looper.myLooper()获取当前线程的Looper对象,通过Looper对象获取MessageQueue对象mQueue=mLooper.mQueue。
sendEmptyMessage(int what)最终调用MessageQueue的enqueueMessage()方法
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
//...
synchronized (this) {
if (mQuitting) {
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
msg.when表示Message应该处理的时间,按照时间顺序维护Message列表。通过mBlocked变量判断当前线程是否阻塞,如果已经阻塞且消息不是异步消息,调用nativeWake(mPtr)方法唤醒当前线程。
4.1 nativeWake
//Looper.cpp
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));
}
}
}
通过往mWakeEventFd描述符写值1,唤醒3.1.3 nativePollOnce节中Looper::pollInner()方法的系统调用epoll_wait(),结束阻塞状态,开始处理消息。
类关系图:
参考链接:
nativePollOnce(); (https://stackoverflow.com/questions/38818642/android-what-is-message-queue-native-poll-once-in-android)
IdleHandler (https://blog.csdn.net/jdsjlzx/article/details/110532500)