編輯:關於android開發
Android是消息驅動的,實現消息驅動有幾個要素:
平時我們最常使用的就是Message與Handler了,如果使用過HandlerThread或者自己實現類似HandlerThread的東西可能還會接觸到Looper,而MessageQueue是Looper內部使用的,對於標准的SDK,我們是無法實例化並使用的(構造函數是包可見性)。
我們平時接觸到的Looper、Message、Handler都是用JAVA實現的,Android做為基於Linux的系統,底層用C、C++實現的,而且還有NDK的存在,消息驅動的模型怎麼可能只存在於JAVA層,實際上,在Native層存在與Java層對應的類如Looper、MessageQueue等。
首先來看一下如果一個線程想實現消息循環應該怎麼做,以HandlerThread為例:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
主要是紅色標明的兩句,首先調用prepare初始化MessageQueue與Looper,然後調用loop進入消息循環。先看一下Looper.prepare。
public static void prepare() {
prepare(true);
}
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));
}
重載函數,quitAllowed默認為true,從名字可以看出來就是消息循環是否可以退出,默認是可退出的,Main線程(UI線程)初始化消息循環時會調用prepareMainLooper,傳進去的是false。使用了ThreadLocal,每個線程可以初始化一個Looper。
再來看一下Looper在初始化時都做了什麼:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
nativeInit();
}
在Looper初始化時,新建了一個MessageQueue的對象保存了在成員mQueue中。MessageQueue的構造函數是包可見性,所以我們是無法直接使用的,在MessageQueue初始化的時候調用了nativeInit,這是一個Native方法:
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return;
}
nativeMessageQueue->incStrong(env);
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
NativeMessageQueue* nativeMessageQueue) {
env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
reinterpret_cast(nativeMessageQueue));
}
在nativeInit中,new了一個Native層的MessageQueue的對象,並將其地址保存在了Java層MessageQueue的成員mPtr中,Android中有好多這樣的實現,一個類在Java層與Native層都有實現,通過JNI的GetFieldID與SetIntField把Native層的類的實例地址保存到Java層類的實例的mPtr成員中,比如Parcel。
再看NativeMessageQueue的實現:
NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
在NativeMessageQueue的構造函數中獲得了一個Native層的Looper對象,Native層的Looper也使用了線程本地存儲,注意new Looper時傳入了參數false。
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
int result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d",
errno);
result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d",
errno);
// Allocate the epoll instance and register the wake pipe.
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", 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 = mWakeReadPipeFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d",
errno);
}
Native層的Looper使用了epoll。初始化了一個管道,用mWakeWritePipeFd與mWakeReadPipeFd分別保存了管道的寫端與讀端,並監聽了讀端的EPOLLIN事件。注意下初始化列表的值,mAllowNonCallbacks的值為false。
mAllowNonCallback是做什麼的?使用epoll僅為了監聽mWakeReadPipeFd的事件?其實Native Looper不僅可以監聽這一個描述符,Looper還提供了addFd方法:
int addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data); int addFd(int fd, int ident, int events, const sp& callback, void* data);
fd表示要監聽的描述符。ident表示要監聽的事件的標識,值必須>=0或者為ALOOPER_POLL_CALLBACK(-2),event表示要監聽的事件,callback是事件發生時的回調函數,mAllowNonCallbacks的作用就在於此,當mAllowNonCallbacks為true時允許callback為NULL,在pollOnce中ident作為結果返回,否則不允許callback為空,當callback不為NULL時,ident的值會被忽略。還是直接看代碼方便理解:
int Looper::addFd(int fd, int ident, int events, const sp& 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 = ALOOPER_POLL_CALLBACK; } int epollEvents = 0; if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; { // acquire lock AutoMutex _l(mLock); Request request; request.fd = fd; request.ident = ident; request.callback = callback; request.data = data; struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = epollEvents; eventItem.data.fd = fd; 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, errno=%d", fd, errno); return -1; } mRequests.add(fd, request); } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); return -1; } mRequests.replaceValueAt(requestIndex, request); } } // release lock return 1; }
如果callback為空會檢查mAllowNonCallbacks看是否允許callback為空,如果允許callback為空還會檢測ident是否>=0。如果callback不為空會把ident的值賦值為ALOOPER_POLL_CALLBACK,不管傳進來的是什麼值。
接下來把傳進來的參數值封裝到一個Request結構體中,並以描述符為鍵保存到一個KeyedVector mRequests中,然後通過epoll_ctl添加或替換(如果這個描述符之前有調用addFD添加監聽)對這個描述符事件的監聽。
類圖:

通過Looper.prepare初始化好消息隊列後就可以調用Looper.loop進入消息循環了,然後我們就可以向消息隊列發送消息,消息循環就會取出消息進行處理,在看消息處理之前,先看一下消息是怎麼被添加到消息隊列的。
在Java層,Message類表示一個消息對象,要發送消息首先就要先獲得一個消息對象,Message類的構造函數是public的,但是不建議直接new Message,Message內部保存了一個緩存的消息池,我們可以用obtain從緩存池獲得一個消息,Message使用完後系統會調用recycle回收,如果自己new很多Message,每次使用完後系統放入緩存池,會占用很多內存的,如下所示:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Message內部通過next成員實現了一個鏈表,這樣sPool就了為了一個Messages的緩存鏈表。
消息對象獲取到了怎麼發送呢,大家都知道是通過Handler的post、sendMessage等方法,其實這些方法最終都是調用的同一個方法sendMessageAtTime:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
sendMessageAtTime獲取到消息隊列然後調用enqueueMessage方法,消息隊列mQueue是從與Handler關聯的Looper獲得的。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
enqueueMessage將message的target設置為當前的handler,然後調用MessageQueue的enqueueMessage,在調用queue.enqueueMessage之前判斷了mAsynchronous,從名字看是異步消息的意思,要明白Asynchronous的作用,需要先了解一個概念Barrier。
Barrier是什麼意思呢,從名字看是一個攔截器,在這個攔截器後面的消息都暫時無法執行,直到這個攔截器被移除了,MessageQueue有一個函數叫enqueueSyncBarier可以添加一個Barrier。
int enqueueSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
在enqueueSyncBarrier中,obtain了一個Message,並設置msg.arg1=token,token僅是一個每次調用enqueueSyncBarrier時自增的int值,目的是每次調用enqueueSyncBarrier時返回唯一的一個token,這個Message同樣需要設置執行時間,然後插入到消息隊列,特殊的是這個Message沒有設置target,即msg.target為null。
進入消息循環後會不停地從MessageQueue中取消息執行,調用的是MessageQueue的next函數,其中有這麼一段:
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());
}
如果隊列頭部的消息的target為null就表示它是個Barrier,因為只有兩種方法往mMessages中添加消息,一種是enqueueMessage,另一種是enqueueBarrier,而enqueueMessage中如果mst.target為null是直接拋異常的,後面會看到。
所謂的異步消息其實就是這樣的,我們可以通過enqueueBarrier往消息隊列中插入一個Barrier,那麼隊列中執行時間在這個Barrier以後的同步消息都會被這個Barrier攔截住無法執行,直到我們調用removeBarrier移除了這個Barrier,而異步消息則沒有影響,消息默認就是同步消息,除非我們調用了Message的setAsynchronous,這個方法是隱藏的。只有在初始化Handler時通過參數指定往這個Handler發送的消息都是異步的,這樣在Handler的enqueueMessage中就會調用Message的setAsynchronous設置消息是異步的,從上面Handler.enqueueMessage的代碼中可以看到。
所謂異步消息,其實只有一個作用,就是在設置Barrier時仍可以不受Barrier的影響被正常處理,如果沒有設置Barrier,異步消息就與同步消息沒有區別,可以通過removeSyncBarrier移除Barrier:
void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
final boolean needWake;
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycle();
}
if (needWake) {
nativeWake(mPtr);
}
}
參數token就是enqueueSyncBarrier的返回值,如果沒有調用指定的token不存在是會拋異常的。
接下來看一下是怎麼MessageQueue的enqueueMessage。
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
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;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
注意上面代碼紅色的部分,當msg.target為null時是直接拋異常的。
在enqueueMessage中首先判斷,如果當前的消息隊列為空,或者新添加的消息的執行時間when是0,或者新添加的消息的執行時間比消息隊列頭的消息的執行時間還早,就把消息添加到消息隊列頭(消息隊列按時間排序),否則就要找到合適的位置將當前消息添加到消息隊列。
消息模型不只是Java層用的,Native層也可以用,前面也看到了消息隊列初始化時也同時初始化了Native層的Looper與NativeMessageQueue,所以Native層應該也是可以發送消息的。與Java層不同的是,Native層是通過Looper發消息的,同樣所有的發送方法最終是調用sendMessageAtTime:
void Looper::sendMessageAtTime(nsecs_t uptime, const sp& handler, const Message& message) { #if DEBUG_CALLBACKS ALOGD("%p ~ sendMessageAtTime - uptime=%lld, 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(); } }
Native Message只有一個int型的what字段用來區分不同的消息,sendMessageAtTime指定了Message,Message要執行的時間when,與處理這個消息的Handler:MessageHandler,然後用MessageEnvelope封裝了time, MessageHandler與Message,Native層發的消息都保存到了mMessageEnvelopes中,mMessageEnvelopes是一個Vector
消息隊列初始化好了,也知道怎麼發消息了,下面就是怎麼處理消息了,看Handler.loop函數:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
loop每次從MessageQueue取出一個Message,調用msg.target.dispatchMessage(msg),target就是發送message時跟message關聯的handler,這樣就調用到了熟悉的dispatchMessage,Message被處理後會被recycle。當queue.next返回null時會退出消息循環,接下來就看一下MessageQueue.next是怎麼取出消息的,又會在什麼時候返回null。
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
if (mQuiting) {
return null;
}
// 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 (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// 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("MessageQueue", "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;
}
}
MessageQueue.next首先會調用nativePollOnce,然後如果mQuiting為true就返回null,Looper就會退出消息循環。
接下來取消息隊列頭部的消息,如果頭部消息是Barrier(target==null)就往後遍歷找到第一個異步消息,接下來檢測獲取到的消息(消息隊列頭部的消息或者第一個異步消息),如果為null表示沒有消息要執行,設置nextPollTimeoutMillis = -1;否則檢測這個消息要執行的時間,如果到執行時間了就將這個消息markInUse並從消息隊列移除,然後從next返回到loop;否則設置nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE),即距離最近要執行的消息還需要多久,無論是當前消息隊列沒有消息可以執行(設置了Barrier並且沒有異步消息或消息隊列為空)還是隊列頭部的消息未到執行時間,都會執行後面的代碼,看有沒有設置IdleHandler,如果有就運行IdleHandler,當IdleHandler被執行之後會設置nextPollTimeoutMillis = 0。
首先看一下nativePollOnce,native方法,調用JNI,最後調到了Native Looper::pollOnce,並從Java層傳進去了nextPollTimeMillis,即Java層的消息隊列中執行時間最近的消息還要多久到執行時間。
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
先不看開始的一大串代碼,先看一下pollInner:
int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
this, mNextMessageUptime - now, timeoutMillis);
#endif
}
// Poll.
int result = ALOOPER_POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// Acquire lock.
mLock.lock();
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error, errno=%d", errno);
result = ALOOPER_POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - timeout", this);
#endif
result = ALOOPER_POLL_TIMEOUT;
goto Done;
}
// Handle all events.
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= ALOOPER_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);
}
}
}
Done: ;
// 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 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 = ALOOPER_POLL_CALLBACK;
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == ALOOPER_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
int callbackResult = response.request.callback->handleEvent(fd, events, data);
if (callbackResult == 0) {
removeFd(fd);
}
// 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 = ALOOPER_POLL_CALLBACK;
}
}
return result;
}
Java層的消息都保存在了Java層MessageQueue的成員mMessages中,Native層的消息都保存在了Native Looper的mMessageEnvelopes中,這就可以說有兩個消息隊列,而且都是按時間排列的。timeOutMillis表示Java層下個要執行的消息還要多久執行,mNextMessageUpdate表示Native層下個要執行的消息還要多久執行,如果timeOutMillis為0,epoll_wait不設置TimeOut直接返回;如果為-1說明Java層無消息直接用Native的time out;否則pollInner取這兩個中的最小值作為timeOut調用epoll_wait。當epoll_wait返回時就可能有以下幾種情況:
出錯返回。
Time Out
正常返回,描述符上有事件產生。
如果是前兩種情況直接goto DONE。
否則就說明FD上有事件發生了,如果是mWakeReadPipeFd的EPOLLIN事件就調用awoken,如果不是mWakeReadPipeFd,那就是通過addFD添加的fd,在addFD中將要監聽的fd及其events,callback,data封裝成了Request對象,並以fd為鍵保存到了KeyedVector mRequests中,所以在這裡就以fd為鍵獲得在addFD時關聯的Request,並連同events通過pushResonse加入mResonse隊列(Vector),Resonse僅是對events與Request的封裝。如果是epoll_wait出錯或timeout,就沒有描述符上有事件,就不用執行這一段代碼,所以直接goto DONE了。
void Looper::pushResponse(int events, const Request& request) {
Response response;
response.events = events;
response.request = request;
mResponses.push(response);
}
接下來進入DONE部分,從mMessageEnvelopes取出頭部的Native消息,如果到達了執行時間就調用它內部保存的MessageeHandler的handleMessage處理並從Native 消息隊列移除,設置result為ALOOPER_POLL_CALLBACK,否則計算mNextMessageUptime表示Native消息隊列下一次消息要執行的時間。如果未到頭部消息的執行時間有可能是Java層消息隊列消息的執行時間小於Native層消息隊列頭部消息的執行時間,到達了Java層消息的執行時間epoll_wait TimeOut返回了,或都通過addFd添加的描述符上有事件發生導致epoll_wait返回,或者epoll_wait是出錯返回。Native消息是沒有Barrier與Asynchronous的。
最後,遍歷mResponses(前面剛通過pushResponse存進去的),如果response.request.ident==ALOOPER_POLL_CALLBACK,就調用注冊的callback的handleEvent(fd,events,data)進行處理,然後從mResonses隊列中移除,這次遍歷完之後,mResponses中保留來來的就都是ident>=0並且callback為NULL的了。在NativeMessageQueue初始化Looper時傳入了mAllowNonCallbacks為false,所以這次處理完後mResponses一定為空。
接下來返回到pollOnce。pollOnce是一個for循環,pollInner中處理了所有response.request.ident==ALOOPER_POLL_CALLBACK的Response,在第二次進入for循環後如果mResponses不為空就可以找到ident>0的Response,將其ident作為返回值返回由調用pollOnce的函數自己處理,在這裡我們是在NativeMessageQueue中調用的Loope的pollOnce,沒對返回值進行處理,而且mAllowNonCallbacks為false也就不可能進入這個循環。pollInner返回值不可能是0,或者說只可能是負數,所以pollOnce中的for循環只會執行兩次,在第二次就返回了。
Native Looper可以單獨使用,也有一個prepare函數,這時mAllowNonCallbakcs值可能為true,pollOnce中對mResponses的處理就有意義了。
在Native Looper的構造函數中,通過pipe打開了一個管道,並用mWakeReadPipeFd與mWakeWritePipeFd分別保存了管道的讀端與寫端,然後用epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem)監聽了讀端的EPOLLIN事件,在pollInner中通過epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis)讀取事件,那是在什麼時候往mWakeWritePipeFd寫,又是在什麼時候讀的mWakeReadPipeFd呢?
在Looper.cpp中我們可以發現如下兩個函數:
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
ssize_t nWrite;
do {
nWrite = write(mWakeWritePipeFd, "W", 1);
} while (nWrite == -1 && errno == EINTR);
if (nWrite != 1) {
if (errno != EAGAIN) {
ALOGW("Could not write wake signal, errno=%d", errno);
}
}
}
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
wake函數向mWakeWritePipeFd寫入了一個“W”字符,awoken從mWakeReadPipeFd讀,往mWakeWritePipeFd寫數據只是為了在pollInner中的epoll_wait可以監聽到事件返回。在pollInner也可以看到如果是mWakeReadPipeFd的EPOLLIN事件只是調用了awoken消耗掉了寫入的字符就往後處理了。
那什麼時候調用wake呢?這個只要找到調用的地方分析一下就行了,先看Looper.cpp,在sendMessageAtTime即發送Native Message的時候,根據發送的Message的執行時間查找mMessageEnvelopes計算應該插入的位置,如果是在頭部插入,就調用wake喚醒epoll_wait,因為在進入pollInner時根據Java層消息隊列頭部消息的執行時間與Native層消息隊列頭部消息的執行時間計算出了一個timeout,如果這個新消息是在頭部插入,說明執行時間至少在上述兩個消息中的一個之前,所以應該喚醒epoll_wait,epoll_wait返回後,檢查Native消息隊列,看頭部消息即剛插入的消息是否到執行時間了,到了就執行,否則就可能需要設置新的timeout。同樣在Java層的MessageQueue中,有一個函數nativeWake也同樣可以通過JNI調用wake,調用nativeWake的時機與在Native調用wake的時機類似,在消息隊列頭部插入消息,還有一種情況就是,消息隊列頭部是一個Barrier,而且插入的消息是第一個異步消息。
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();//如果頭部是Barrier並且新消息是異步消息則“有可能”需要喚醒
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;
}
在頭部插入消息不一定調用nativeWake,因為之前可能正在執行IdleHandler,如果執行了IdleHandler,就在IdleHandler執行後把nextPollTimeoutMillis設置為0,下次進入for循環就用0調用nativePollOnce,不需要wake,只有在沒有消息可以執行(消息隊列為空或沒到執行時間)並且沒有設置IdleHandler時mBlocked才會為true。
如果Java層的消息隊列被Barrier Block住了並且當前插入的是一個異步消息有可能需要喚醒Looper,因為異步消息可以在Barrier下執行,但是這個異步消息一定要是執行時間最早的異步消息。
退出Looper也需要wake,removeSyncBarrier時也可能需要
Android開發錯誤匯總,android開發匯總
Android開發錯誤匯總,android開發匯總【錯誤信息】 [2011-01-19 16:39:10 - ApiDemos] WARNING: Application
android之sharedpreference的兩種使用方法,sharedpreference
android之sharedpreference的兩種使用方法,sharedpreferencesharedPreferences的介紹: 1.sharedPrefere
Android中通過訪問本地相冊或者相機設置用戶頭像,android用戶頭像
Android中通過訪問本地相冊或者相機設置用戶頭像,android用戶頭像目前幾乎所有的APP在用戶注冊時都會有設置頭像的需求,大致分為三種情況: (1)通過獲取本地相
TabWidgetDemo,androidwidgetdemo
TabWidgetDemo,androidwidgetdemoTabWidget可以通過不同的標簽進行切換並且顯示不同的內容,相當於Button按鈕實現不同的功能。 Ta