对于应用来说,Android系统中应用都是通过消息机制驱动的,其工作原理大致如下:
Ø 有一个消息队列,可以往这个消息队列中投递消息。
Ø 有一个消息循环,不断从当前的消息队列中获取消息,然后处理。
通过上图,大致可以看出:
Ø 事件源把需要处理的消息加入到消息队列中,一般是添加到消息队列的尾部,一些优先级高的消息也可以加至队列头。
Ø 处理线程不断从消息队列的头部取出消息并处理。
这些工作主要是由Handler和Looper来实现的:
Ø Looper类,封装了消息循环,并且有一个消息队列。
Ø Handler类,封装了消息的投递,消息的处理等接口。
先了解下所涉及到的类,以及相互之间的关系(下图中只列出了主要函数成员):
1.1 Looper类的分析
先来看一个常用的looper方面的例子:
// (线程2)
class LooperThread extends Thread {
publicHandler mHandler;
public void run() {
// 调用prepare
Looper.prepare();
......
// 进入消息循环
Looper.loop();
}
}
// 应用程序使用LooperThread (线程1)
{
......
LooperThread looperThread = new LooperThread();
looperThread.start(); // 启动新线程,线程函数是run
}
Looper与一个线程进行绑定,先通过Looper.prepare()初始化,然后通过Looper.loop()进入消息循环。
Looper.prepare的代码如下:
[-->Looper.java::prepare]
private static void prepare(boolean quitAllowed) {
// 一个Looper只能调用一次prepare,或者就抛出异常
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 构造一个Looper对象,并绑定到当前调用线程中(设置到调用线程的局部变量中)
sThreadLocal.set(new Looper(quitAllowed));
}
ThreadLocal是Java中的线程局部变量类。它的实现和操作系统提供的线程本地存储(TLS)有关系。总之,该类有两个关键函数:
Ø set:设置调用线程的局部变量。
Ø get:获取调用线程的局部变量。
根据上面的分析可知,prepare会在调用线程的局部变量中设置一个Looper对象。先看看Looper对象的构造,其代码如下所示:
[-->Looper.java::Looper]
private Looper(boolean quitAllowed) {
// 创建一个消息队列
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
prepare函数主要做了一件事:
在调用prepare的线程中,设置了一个Looper对象,并把该Looper对象保存在调用线程的TLS中。而Looper对象内部封装了一个消息队列。也就是说,prepare函数通过ThreadLocal机制,巧妙地把Looper和调用线程关联在一起了。
再来看下Looper.loop循环部分的主要代码:
[-->Looper.java::loop]
public static void loop() {
final Looper me = myLooper(); // 返回保存在调用线程TLS中的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 取出这个Looper的消息队列
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 调用该消息的Handler,交给它的dispatchMessage函数处理
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
//myLooper函数返回调用线程的线程局部变量,也就是存储在其中的Looper对象
[-->Looper.java::myLooper]
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
通过上面的分析会发现,Looper的作用是:
Ø Looper封装了一个消息队列。
Ø Looper的prepare函数把这个Looper和调用prepare的线程(即最终的处理线程)绑定在一起了。
Ø 处理线程调用loop函数,处理来自该消息队列的消息。
当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么,该消息就将由和Looper绑定的处理线程来处理。那么,事件源又是怎么向Looper消息队列添加消息的呢?带着这个疑问我们继续分析。
1.2 Handler类的分析
如果不知道Handler,这里有一个很原始的方法向Looper消息队列里添加消息:
Ø 调用Looper的myQueue,它将返回消息队列对象MessageQueue。
Ø 构造一个Message,填充它的成员,尤其是target变量。
Ø 调用MessageQueue的enqueueMessage,将消息插入消息队列。
这种原始方法的确很麻烦,且极容易出错。但有了Handler后,我们的工作就变得异常简单了。Handler更像一个辅助类,帮助我们简化编程的工作。
Handle提供了一系列函数,帮助我们完成创建消息和插入消息队列的工作,在Handler的构造函数里,通过Looper获取其mQueue,这样就可通过obtainMessage生成一个Message对象,再把该Message通过sendMessage等一系列的接口(见下图)添加到消息队列中。这样开发者就只需关注两个函数obtainMessage和sendMessage,而不需要再做其他的额外操作,从而解决了编写代码的复杂度,以及降低出错的概率。
Handler消息处理的过程:
dispatchMessage定义了一套消息处理的优先级,它们分别是:
Ø Message如果自带了callback处理,则交给callback处理。
Ø Handler如果设置了全局的mCallback,则交给mCallback处理。
Ø 如果上述都没有,该消息则会被交给Handler子类实现的handleMessage来处理,当然,这需要从Handler派生并重载handleMessage函数。
在通常情况下,我们一般都是采用第三种方法,即在子类中重载handleMessage来完成处理工作的。
1.3 HandlerThread类的分析
我们知道,handler和looper对象是在两个不同线程中线程中创建的,而且handler又要拥有looper对象,这又牵涉到了一个问题,多线程之间的同步机制。这样子对于应用的开发又多了一份工作,Android系统内也考虑到这点,于是就对外封装了另一个辅助类HandlerThread。
这里来看下两个线程之间是怎么同步的:
[-->HandlerThread .java]
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare(); // 创建这个线程上的Looper
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); // 通知获取Looper的线程,此时Looper已经创建好了。
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait(); // 如果新线程还未创建Looper,则等待
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
}
通过上面的代码可以了解到HandlerThread中是采用wait和notifyAll的机制来实现线程之间的同步,当mLooper还为空时,就wait,直到mLooper被创建完成,这样就有效地防止mLooper还没有生成之前,另一个线程就开始使用该变量。
分析到这里,已经大致可以了解Handler和Looper的工作原理了,其流程可见下图。
1.4 MessageQueue的分析
在Looper.loop的分析中,我们还遗留一个问题,queue.next()具体做了哪些动作呢?接下来我们将会详细分析。
在创建Looper的时候,在Looper里会创建一个MessageQueue:
[-->MessageQueue.java::MessageQueue]
MessageQueue() {
mPtr = nativeInit(); // 构造函数调用nativeInit,该函数由Native层实现
}
nativeInit函数的真正实现为android_os_MessageQueue_nativeInit,其代码如下:
[-->android_os_MessageQueue.cpp::android_os_MessageQueue_nativeInit]
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
// NativeMessageQueue是MessageQueue在Native层的代表
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
......
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue); // 将NativeMessageQueue返回Java层
}
nativeInit函数在Native层创建了一个与MessageQueue对应的NativeMessageQueue对象:
[-->android_os_MessageQueue.cpp::NativeMessageQueue]
NativeMessageQueue::NativeMessageQueue() {
/*一个线程会有一个Looper来循环处理消息队列中的消息。并且这个Looper对象保存
在线程本地存储空间(Thread Local Storage TLS)中*/
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
Looper的构造函数比较简单,首先构造一个pipe,一端用于读,另一端用于写。然后使用epoll将它mWakeReadPipeFd添加到mEpollFd中。后面我们就可以通过在mWakeWritePipeFd端写数据,让epoll_wait跳出等待。
[-->Looper.cpp::Looper]
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);
mIdling = false;
// 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);
}
当一切初始化完成后,Java层的消息循环处理,也就是Looper会在一个循环中提取并处理消息。消息的提取就是调用MessageQueue.next函数。当消息队列为空时,next函数进入阻塞状态。
在分析next函数之前,我们先分析下如何将一个Message投递到MessageQueue中,其代码如下:
[-->MesssageQueue.java::enqueueMessage]
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
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 {
// 消息队列中还有剩余消息,将新的msg按照时间顺序插入到消息队列中
}
if (needWake) {
nativeWake(mPtr); // 调用nativeWake,以触发nativePollOnce函数结束等待
}
}
return true;
}
nativeWake的调用就类似于nativeInit,调用native层的nativeWake,再进入NativeMessageQueue::wake函数,最终调用Looper::wake():
[-->Looper.cpp::wake]
void Looper::wake() {
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);
}
}
}
wake函数仅仅向管道的写端写入一个字符“W”,这样管道的读端就会因为有数据可读而从等待状态中醒来。
MessageQueue还允许向其消息队列中通过enqueueSyncBarrier设置一个拦截器(Barrier Message),这个Message同样需要设置执行时间,然后插入到消息队列,特殊的是这个Message没有设置target,即msg.target为null。在这个拦截器后面的消息都暂时无法执行,直到这个拦截器被移除了。
另外在MessageQueue中还有个异步消息的概念。所谓的异步消息其实就是,我们可以通过enqueueBarrier往消息队列中插入一个Barrier,那么队列中执行时间在这个Barrier以后的同步消息都会被这个Barrier拦截住无法执行,直到我们调用removeBarrier移除了这个Barrier,而异步消息则没有影响,消息默认就是同步消息,除非我们调用了Message的setAsynchronous,这个方法是隐藏的。只有在初始化Handler时通过参数指定往这个Handler发送的消息都是异步的,这样在Handler的enqueueMessage中就会调用Message的setAsynchronous设置消息是异步的。
了解了enqueueMessage 的工作后,现在就开始分析next的工作流程。MessageQueue同时支持Java层和Native层的事件,其next函数的实现代码如下:
[-->MessagQueue.java::next]
Message next() {
…...
// 进入next时,开始设置 nextPollTimeoutMillis为0,让nativePollOnce不阻塞
int nextPollTimeoutMillis = 0;
for (;;) {
…...
// mPtr保存了NativeMessageQueue的指针,调用nativePollOnce进行等待
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 (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg; // 返回一个Message给Looper进行派发和处理
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
......
}
nextPollTimeoutMillis = 0;
}
}
队列被激活之后,如果头部消息是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。如果第一个待处理的消息还没有到要处理的时间则设置激活等待时间;否则这个消息就是需要处理的消息,将该消息设置为inuse,并将队列设置为非blocked状态,然后返回该消息。
nativePollOnce的实现函数是android_os_MessageQueue_nativePollOnce,代码如下:
[-->android_os_MessageQueue.cpp::android_os_MessageQueue_nativePollOnce]
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz,
jlong ptr, jint timeoutMillis) {
// 获取NativeMessageQueue对象,并调用它的pollOnce
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, timeoutMillis);
}
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis); // 调用Looper的pollOnce函数
}
[-->Looper.h::pollOnce]
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData),其中:
timeoutMillis参数为超时等待时间。如果值为–1,则表示无限等待,直到有事件发生为止。如果值为0,则无须等待立即返回。
outFd用来存储发生事件的那个文件描述符。
outEvents用来存储在该文件描述符上发生了哪些事件,目前支持可读、可写、错误和中断4个事件。这4个事件其实是从epoll事件转化而来的。
outData用于存储上下文数据,这个上下文数据是由用户在添加监听句柄时传递的,它的作用和pthread_create函数最后一个参数param一样,用来传递用户自定义的数据。
另外,pollOnce函数的返回值也具有特殊的意义,具体如下:
Ø 当返回值为ALOOPER_POLL_WAKE时,表示这次返回是由wake函数触发的,也就是管道写端的那次写事件触发的。
Ø 返回值为ALOOPER_POLL_TIMEOUT表示等待超时。
Ø 返回值为ALOOPER_POLL_ERROR表示等待过程中发生错误。
Ø 返回值为ALOOPER_POLL_CALLBACK表示某个被监听的句柄因某种原因被触发。这时,outFd参数用于存储发生事件的文件句柄,outEvents用于存储所发生的事件。
关于epoll方面的工作原理及相关知识,这里就不再描述,可自行查阅Linux方面的书籍。
[-->Looper.cpp::pollOnce]
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
//mResponses是一个Vector,这里首先需要处理response
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
// ident的值只能>=0或者POLL_CALLBACK,如果request含有callback,则ident被强制设置为POLL_CALLBACK,此种情况在pollInner中处理了,所以此处只需要处理>= 0的情况,由于没有callback,系统也不知道如何处理,pollOnce只是返回它的ident。
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
看了上述代码片段,或许还是不清楚pollOnce到底做了些什么事情,甚至对mResponses也是云里雾里,那么我们接下来先分析下poolInner函数,然后再回过头来看看本段代码。
poolInner代码非常长,我们将其调试代码去掉后,如下:
[-->Looper.cpp::pollInner]
int Looper::pollInner(int timeoutMillis) {
//根据Native Message的信息计算此次需要等待的时间
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;
}
}
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mIdling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mIdling = false;
// Acquire lock.
mLock.lock();
// 返回值小于零,表示发生错误
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
ALOGW("Poll failed with an unexpected error, errno=%d", errno);
result = POLL_ERROR;
goto Done;
}
// eventCount为零,表示发生超时,因此直接跳转到Done
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
// eventCount表示发生事件的个数
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(); // 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 |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
//每处理一个Request,就相应构造一个Response
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
//除了处理Request外,还处理Native的Message
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;
}
}
// Release lock.
mLock.unlock();
//处理那些带回调函数的Response
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;
int callbackResult = response.request.callback->handleEvent(fd, events, data);
// callback函数的返回值很重要,如果为0,表明不需要再次监视该文件句柄
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 = POLL_CALLBACK;
}
}
return result;
}
poolInner首先处理Native的Message,调用Native Handler的handleMessage处理该Message。处理完Native Message后,接着处理Native的Request,等都处理完了,才返回到java层,继续处理java的Message。
1.5 实例
HandlerThread mCheckMsgThread= new HandlerThread("check-message-coming");
mCheckMsgThread.start();
mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper(), new Callback() {
@Override
public boolean handleMessage(Message msg) {
}
});
mMainHandler = new Handler(new Callback() {
@Override
public boolean handleMessage(Message msg) {
}
});
mMainHandler.sendEmptyMessage(123);
mMainHandler.post(new Runnable() {
public void run() {
}
});
1.6 小结
Java层提供了Looper类和MessageQueue类,其中Looper类提供循环处理消息的机制,MessageQueue类提供一个消息队列,以及插入、删除和提取消息的函数接口。另外,Handler也是在Java层常用的与消息处理相关的类。
MessageQueue内部通过mPtr变量保存一个Native层的NativeMessageQueue对象,mMessages保存来自Java层的Message消息。
NativeMessageQueue保存一个native的Looper对象,提供pollOnce和addFd等函数。
Java层有Message类和Handler类,而Native层对应也有Message类和Message-Handler抽象类。在编码时,一般使用的是MessageHandler的派生类WeakMessage-Handler类。