* 站在巨人的肩膀上可以看的更遠 *
Android 9.0 Native Looper機制(原理篇)
Android 9.0 Native Looper機制(應用篇)
前言
在分析Android Framework Native層代碼的時候,很多地方都用到了Android系統中重要的輔助類Looper來進行線程間通信或設計處理邏輯,本文將深入分析一下Looper機制,方便理解其運行原理。
- 先給出幾篇非常值得參考的文章
https://blog.csdn.net/xiaosayidao/article/details/73992078
- 另外要理解Looper機制的原理,還需要額外理解Linux中的epool機制
https://blog.csdn.net/xiajun07061225/article/details/9250579
- Linux中的eventfd的使用:
https://blog.csdn.net/qq_28114615/article/details/97929524
基本類圖
Android Framework Native層的消息Looper機制代碼,主要實現位於:
system/core/libutils/Looper.cpp
system/core/include/utils/Looper.h
基本類圖如下:
基本對象說明
- Message : 消息的載體,代表了一個事件,通過一個what字段來標記是什么事件,源碼定義如下:

/** * 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; };
- MessageHandler/WeakMessageHandler 消息處理的接口(基類), 子類通過實現handleMessage來實現特定Message的處理邏輯。WeakMessageHandler包含了一個MessageHandler的弱指針

/** * 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; }; /** * A simple proxy that holds a weak reference to a message handler. */
class WeakMessageHandler : public MessageHandler { protected: virtual ~WeakMessageHandler(); public: WeakMessageHandler(const wp<MessageHandler>& handler); virtual void handleMessage(const Message& message); private: wp<MessageHandler> mHandler; };
- LooperCallback/SimpleLooperCallback : 用於Looper回調,實際上就是保存一個Looper_callbackFunc指針的包裝基類。在Looper::addFd()方法添加監測的fd時來設置回調。

/** * A looper callback. */
class LooperCallback : public virtual RefBase { protected: virtual ~LooperCallback(); public: /** * Handles a poll event for the given file descriptor. * It is given the file descriptor it is associated with, * a bitmask of the poll events that were triggered (typically EVENT_INPUT), * and the data pointer that was originally supplied. * * Implementations should return 1 to continue receiving callbacks, or 0 * to have this file descriptor and callback unregistered from the looper. */
virtual int handleEvent(int fd, int events, void* data) = 0; }; /** * Wraps a Looper_callbackFunc function pointer. */
class SimpleLooperCallback : public LooperCallback { protected: virtual ~SimpleLooperCallback(); public: SimpleLooperCallback(Looper_callbackFunc callback); virtual int handleEvent(int fd, int events, void* data); private: Looper_callbackFunc mCallback; };
- Looper核心類,它其中維護着一個消息/監測的fd隊列,當用戶調用pollOnce或pollAll時,就會去判斷是否有消息要處理(調用對應的handler::handleMessage)或監測對的fd有事件發生(回調對應的callback函數)
關鍵方法分析
- Create Looper == 創建Looper的方法
Looper提供兩種方式創建Looper:
1. 直接調用Looper 構造函數:

Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false), mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s", strerror(errno)); AutoMutex _l(mLock); rebuildEpollLocked(); }
2. 調用Looper的靜態函數 prepare() :如果線程已經有對應的Looper,則直接返回,否則才會創建新的Looper。

sp<Looper> Looper::prepare(int opts) { bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS; sp<Looper> looper = Looper::getForThread(); if (looper == NULL) { looper = new Looper(allowNonCallbacks); Looper::setForThread(looper); } if (looper->getAllowNonCallbacks() != allowNonCallbacks) { ALOGW("Looper already prepared for this thread with a different value for the "
"LOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); } return looper; }
Looper的構造函數里主要做兩件事情:
- > 調用eventfd(0, EFD_NONBLOCK)返回mWakeEventFd,用於喚醒epoll_wait()
- > 調用rebuildEpollLocked() 創建epoll 文件描述符,並將mWakeEventFd加入到epoll監聽隊列中

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 close(mEpollFd); } // Allocate the new epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno)); 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; int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s", strerror(errno)); for (size_t i = 0; i < mRequests.size(); i++) { const Request& request = mRequests.valueAt(i); struct epoll_event eventItem; request.initEventItem(&eventItem); int epollResult = epoll_ctl(mEpollFd, 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)); } } }
注:參數allowNonCallbacks表明是否可以在Looper_addFd時不提供callback
- Send Message == 發送消息的方法
發送消息是指將消息插入到消息隊列 mMessageEnvelopes。mMessageEnvelopes 里面的是根據時間順序排列存放MessageEnvlope:下標越小,越早被處理。
發送消息的函數有如下三個,但最終都是調用sendMessageAtTime() 來實現的。

/** * Enqueues a message to be processed by the specified handler. * * The handler must not be null. * This method can be called on any thread. */
void sendMessage(const sp<MessageHandler>& handler, const Message& message); /** * Enqueues a message to be processed by the specified handler after all pending messages * after the specified delay. * * The time delay is specified in uptime nanoseconds. * The handler must not be null. * This method can be called on any thread. */
void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, const Message& message); /** * Enqueues a message to be processed by the specified handler after all pending messages * at the specified time. * * The time is specified in uptime nanoseconds. * The handler must not be null. * This method can be called on any thread. */
void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, const Message& message);
來看一下sendMessageAtTime()函數的具體實現:

void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, const Message& message) { #if DEBUG_CALLBACKS ALOGD("%p ~ sendMessageAtTime - uptime=%" PRId64 ", handler=%p, what=%d", this, uptime, handler.get(), message.what); #endif size_t i = 0; { // acquire lock
AutoMutex _l(mLock); size_t messageCount = mMessageEnvelopes.size(); while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { i += 1; } MessageEnvelope messageEnvelope(uptime, handler, message); mMessageEnvelopes.insertAt(messageEnvelope, i, 1); // Optimization: If the Looper is currently sending a message, then we can skip // the call to wake() because the next thing the Looper will do after processing // messages is to decide when the next wakeup time should be. In fact, it does // not even matter whether this code is running on the Looper thread.
if (mSendingMessage) { return; } } // release lock // Wake the poll loop only when we enqueue a new message at the head.
if (i == 0) { wake(); } }
首先根據uptime在mMessageEnvelopes遍歷,找到合適的位置,並將message 封裝成MessageEnvlope,插入找到的位置上。
然后決定是否要喚醒Looper:
-
- 如果Looper此時正在派發message,則不需要wakeup Looper。因為這一次looper處理完消息之后,會重新估算下一次epoll_wait() 的wakeup時間。
- 如果是插在消息隊列的頭部,則需要立即wakeup Looper
另外還有提供移除消息的方法removeMessages:

/** * Removes all messages for the specified handler from the queue. * * The handler must not be null. * This method can be called on any thread. */
void removeMessages(const sp<MessageHandler>& handler); /** * Removes all messages of a particular type for the specified handler from the queue. * * The handler must not be null. * This method can be called on any thread. */
void removeMessages(const sp<MessageHandler>& handler, int what);
- Add fd == 添加fd的方法
添加新的文件描述符並設置回調方法,用於監測事件,提供了兩種方法:

/** * Adds a new file descriptor to be polled by the looper. * If the same file descriptor was previously added, it is replaced. * * "fd" is the file descriptor to be added. * "ident" is an identifier for this event, which is returned from pollOnce(). * The identifier must be >= 0, or POLL_CALLBACK if providing a non-NULL callback. * "events" are the poll events to wake up on. Typically this is EVENT_INPUT. * "callback" is the function to call when there is an event on the file descriptor. * "data" is a private data pointer to supply to the callback. * * There are two main uses of this function: * * (1) If "callback" is non-NULL, then this function will be called when there is * data on the file descriptor. It should execute any events it has pending, * appropriately reading from the file descriptor. The 'ident' is ignored in this case. * * (2) If "callback" is NULL, the 'ident' will be returned by Looper_pollOnce * when its file descriptor has data available, requiring the caller to take * care of processing it. * * Returns 1 if the file descriptor was added, 0 if the arguments were invalid. * * This method can be called on any thread. * This method may block briefly if it needs to wake the poll. * * The callback may either be specified as a bare function pointer or as a smart * pointer callback object. The smart pointer should be preferred because it is * easier to avoid races when the callback is removed from a different thread. * See removeFd() for details. */
int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data); int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);
來看一下具體實現:

int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { #if DEBUG_CALLBACKS ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, events, callback.get(), data); #endif
if (!callback.get()) { if (! mAllowNonCallbacks) { ALOGE("Invalid attempt to set NULL callback but not allowed for this looper."); return -1; } if (ident < 0) { ALOGE("Invalid attempt to set NULL callback with ident < 0."); return -1; } } else { ident = POLL_CALLBACK; } { // acquire lock
AutoMutex _l(mLock); Request request; request.fd = fd; request.ident = ident; request.events = events; request.seq = mNextRequestSeq++; request.callback = callback; request.data = data; if (mNextRequestSeq == -1) mNextRequestSeq = 0; // reserve sequence number -1
struct epoll_event eventItem; request.initEventItem(&eventItem); ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex < 0) { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno)); return -1; } mRequests.add(fd, request); } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { if (errno == ENOENT) { // Tolerate ENOENT because it means that an older file descriptor was // closed before its callback was unregistered and meanwhile a new // file descriptor with the same number has been created and is now // being registered for the first time. This error may occur naturally // when a callback has the side-effect of closing the file descriptor // before returning and unregistering itself. Callback sequence number // checks further ensure that the race is benign. //
// Unfortunately due to kernel limitations we need to rebuild the epoll // set from scratch because it may contain an old file handle that we are // now unable to remove since its file descriptor is no longer valid. // No such problem would have occurred if we were using the poll system // call instead, but that approach carries others disadvantages.
#if DEBUG_CALLBACKS ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
"being recycled, falling back on EPOLL_CTL_ADD: %s", this, strerror(errno)); #endif epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error modifying or adding epoll events for fd %d: %s", fd, strerror(errno)); return -1; } scheduleEpollRebuildLocked(); } else { ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno)); return -1; } } mRequests.replaceValueAt(requestIndex, request); } } // release lock
return 1; }
主要做了3件事:
1. 把要監測的文件描述符fd、事件標識events、回調函數指針callback、一些額外參數data封裝成一個Request對象;
2. 調用epoll_ctl對文件描述符fd進行監測;
3. 把Request對象添加到mRequests列表(KeyedVector<int, Request> mRequests)
- Poll Looper == 輪詢處理的方法
要讓Looper運行起來才能處理消息。Looper提供了接口:pollOnce()
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
調用poolOnce返回,返回結果如下四種情況

enum { /** * Result from Looper_pollOnce() and Looper_pollAll(): * The poll was awoken using wake() before the timeout expired * and no callbacks were executed and no other file descriptors were ready. */ POLL_WAKE = -1, // 在超時前通過wake()喚醒,沒有callback被執行,且沒有文件描述符事件 /** * Result from Looper_pollOnce() and Looper_pollAll(): * One or more callbacks were executed. */ POLL_CALLBACK = -2, // 一個或多個callback被執行 /** * Result from Looper_pollOnce() and Looper_pollAll(): * The timeout expired. */ POLL_TIMEOUT = -3, // 超時 /** * Result from Looper_pollOnce() and Looper_pollAll(): * An error occurred. */ POLL_ERROR = -4, // 發生錯誤 };
如果Looper中沒有任何要處理的event/message,則會阻塞在epoll_wait() 等待事件到來。
調用流程:pollOnce() -> pollInner() -> epoll_wait()

struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
epoll_wait()其有三種情況會返回,返回值eventCount為上來的epoll event數量。
- 出現錯誤返回, eventCount < 0;
- timeout返回,eventCount = 0,表明監聽的文件描述符中都沒有事件發生,將直接進行native message的處理;
- 監聽的文件描述符中有事件發生導致的返回,eventCount > 0; 有eventCount 數量的epoll event 上來。
epoll_wait喚醒后,接下首先是判斷是否有監測的文件描述符事件,並把事件標識events和對應封裝為Response對象,加入到mResponses隊列中(Vector<Response> mResponses)

for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeEventFd) { if (epollEvents & EPOLLIN) { awoken(); } else { ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; if (epollEvents & EPOLLIN) events |= EVENT_INPUT; if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT; if (epollEvents & EPOLLERR) events |= EVENT_ERROR; if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP; pushResponse(events, mRequests.valueAt(requestIndex)); } else { ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd); } } }
然后 開始遍歷消息隊列,判斷是否有消息到了處理時間了,並調用消息對應的handler->handleMessage(message);

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(); #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", this, handler.get(), message.what); #endif 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; } }
同樣會遍歷mResponses隊列,並回調對應的response.request.callback->handleEvent(fd, events, data)

// Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) { Response& response = mResponses.editItemAt(i); if (response.request.ident == POLL_CALLBACK) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, response.request.callback.get(), fd, events, data); #endif
// Invoke the callback. Note that the file descriptor may be closed by // the callback (and potentially even reused) before the function returns so // we need to be a little careful when removing the file descriptor afterwards.
int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd, response.request.seq); } // Clear the callback reference in the response structure promptly because we // will not clear the response vector itself until the next poll.
response.request.callback.clear(); result = POLL_CALLBACK; } }
注:每個消息處理完,會被從消息隊列移除 mMessageEnvelopes.removeAt(0);
每個fd事件回調完,也會被從mRequests列表中移除
對於pollInner()
調整timeout:Adjust the timeout based on when the next message is due.
mNextMessageUptime 是 消息隊列 mMessageEnvelopes 中最近一個即將要被處理的message的時間點。
所以需要根據mNextMessageUptime 與 調用者傳下來的timeoutMillis 比較計算出一個最小的timeout,這將決定epoll_wait() 可能會阻塞多久才會返回。
epoll_wait()
處理epoll_wait() 返回的epoll events.
判斷epoll event 是哪個fd上發生的事件
如果是mWakeEventFd,則執行awoken(). awoken() 只是將數據read出來,然后繼續往下處理了。其目的也就是使epoll_wait() 從阻塞中返回。
如果是通過Looper.addFd() 接口加入到epoll監聽隊列的fd,並不是立馬處理,而是先push到mResponses,后面再處理。
處理消息隊列 mMessageEnvelopes 中的Message.
如果還沒有到處理時間,就更新一下mNextMessageUptime
處理剛才放入mResponses中的 事件.
只處理ident 為POLL_CALLBACK的事件。其他事件在pollOnce中處理
基本運行機制
大概畫一個抽象的處理概念圖示,不一定准確
- 創建消息Message並指定指定MessageHandler, 調用sendMessage()把消息傳遞給Looper。
- Looper根據Message 和 MessageHandler創建MessageEnvelope 。然后將MessageEnvelope 添加到Looper的消息隊列 mMessageEnvelopes 中。
- Native Looper除了處理Message之外,還可以監聽指定的文件描述符。
- 通過addFd() 添加要監聽的fd到epoll的監聽隊列中,並將傳進來的fd,ident,callback,data 封裝成Request 對象,然后加入到Looper 的mRequests 中。
- 外部邏輯(可以是某一個Thread)不斷調用poolOnce-> pollInner() -> epoll_wait()阻塞,等待事件發生或超時
- 當該fd有事件發生時,epoll_wait()會返回epoll event,然后從mRequests中找到對應的request對象,並加上返回的epoll event 類型(EPOLLIN、EPOLLOUT…)封裝成Response對象,加入到mResponses 中。
- 然后在需要處理Responses的時候,從mResponses遍歷取出Response進行處理。
- 同樣遍歷消息隊列 mMessageEnvelopes中的消息進行處理
- 如此不斷循環