引言
相信大家在面试的时候,提到多线程,肯定会涉及到Handler相关的知识点。比如下面常见的问题:
- Handler的基本原理
- 子线程中怎么使用handler
- MessageQueue是怎么获取消息的
- 非UI线程真的不能操作View吗
- Handler导致的内存泄漏,以及处理办法
- Handler消息延迟是怎么处理的
还有很多跟Handler相关的问题,上面我只列举了其中的一些,不管面试官问什么问题,只要我们对handler的源码很熟悉,以及源码中所涉及到的知识点有很好的扩展,那么在面试的过程中,对于Handler这个知识点来说,我们就是一个加分项。下面我们来说一下Android中的Handler是如何实现的?
Android应用程序的消息处理机制是围绕消息队列来实现的。一个线程拥有一个消息队列之后,就可以进入到一个消息循环中,同时其他线程也可以向这个消息队列发送消息,以便可以在这个消息被处理时执行一个特定的操作。这样我们就可以将一个线程的生命周期分为创建消息队列和进入消息循环两个阶段。其中,消息循环队列又分为发送消息和处理消息两个阶段。
1.创建消息队列
Android中通过调用Looper类中的prepare和prepareMainLooper两个静态成员函数来创建MessageQueue。其中,prepareMainLooper是用来为主线程创建消息队列,prepare是用来为子线程创建消息队列。
Android中的消息处理机制不禁可以在Java层使用,还可以在C++代码中使用。Java层的Looper类和MessageQueue类是通过C++层的Looper类和NativeMessageQueue类来实现的。在MessageQueue中有一个mPtr的成员变量。它保存了C++层的NativeMessageQueue对象的地址值。这样就可以将Java层的MessageQueue与C++层的NativeMessageQueue对象关联起来。具体关系如下图所示:
在MessageQueue类中包含一个类型为Message的mMessage,我们可以通过调用MessageQueue中的equeueMessage函数来向mMessage中添加一个消息。在创建MessageQueue的时候会调用nativeInit方法并将返回值赋予mPtr。通过调用nativeInit方法会在C++层创建NativeMessageQueue对象和Looper对象。在创建C++层的Looper对象时会创建一个epoll管道,并将该管道的读端文件描述符和写段文件描述符保存在它的成员变量mWakeReadPipedFd和mWakeWritepipedFd中。
ThreadLocal是线程内部的存储类,通过它可以在指定的线程中存储数据。只有在指定的线程中可以获取到存储的数据,其他线程是不能够获取到存储的数据。
在Looper类中定义了一个类型为ThreadLocal的静态成员变量sThreadLocal,通过它来保存线程中的Looper对象。ThreadLocal内部会通过静态内部类ThreadLocalMap来实现线程跟looper的绑定关系,每一个新创建的线程作为map的key,每个线程对应的Looper作为map的value。我们可以通过set函数将新创建的线程和该线程的Looper对象进行关联 ,通过get方法或者Looper类的静态成员函数myLooper来获取当前线程对应的Looper对象。具体代码如下:
public final class Looper {
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@UnsupportedAppUsage
private static Looper sMainLooper;
@UnsupportedAppUsage
final MessageQueue mQueue;
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));
}
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
...
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
}
Looper类的静态成员函数prepareMainLooper只能再Android应用程序的主线程中调用。Android应用程序主线程是一个特殊的线程,因为只有它可以执行与UI相关的操作,因为我们也将他成为UI线程。将Android应用程序主线程的Looper对象单独保存到一个静态成员变量中,是为了让其他线程可以通过Looper类的静态成员函数getMainLooper来访问它,从而可以往它的消息队列中发送一些与UI操作相关的消息。
在Looper类的构造方法中会创建一个全局的MessageQueue成员变量,在创建MessageQueue的时候又会通过构造函数中的nativeInit函数在C++层创建一个NativeMessageQueue对象。
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
return reinterpret_cast<jlong>(nativeMessageQueue);
}
nativeInit会将新创建的NativeMessageQueue的地址值返回给MessageQueue中的变量mPtr,这样MessageQueue就跟C++层的NativeMessageQueue进行绑定。
NativeMessageQueue中包含一个gMessageQueueClassInfo的结构体。
static struct {
jfieldID mPtr; // native object attached to the DVM MessageQueue
jmethodID dispatchEvents;
} gMessageQueueClassInfo;
int register_android_os_MessageQueue(JNIEnv* env) {
int res = RegisterMethodsOrDie(env, "android/os/MessageQueue", gMessageQueueMethods,
NELEM(gMessageQueueMethods));
jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");
gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");
gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz,
"dispatchEvents", "(II)I");
return res;
}
通过register_android_os_MessageQueue方法我们可以看到,gMessageQueueClassInfo中的mPtr就是对应Java层中的MessageQueue中的成员变量mPtr的偏移量,通过这个偏移量就可以把C++创建的NativeMessageQueue对象的地址值保存在Java层创建的MessageQueue的成员变量mPtr中,后面我们可以利用这个变量获取对应的的NativeMessageQueue实例。
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
在创建NativeMessageQueue实例的时候,会创建一个C++层的Looper对象,并将这个对象赋值给当前类mLooper变量,同时调用C++层的Looper类的静态成员函数setForThread将它与当前线程关联起来。
下面看一下C++层的Looper类,此处需注意,android5.0之后Looper位于“/system/core/libutils/Looper.cpp”,android5.0之前位于“frameworks/base/libs/utils/Looper.cpp”
下面我们分为两个版本来介绍C++层的Looper对象。
- android6.0之前
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
int wakeFds[2];
//创建一个pipe管道,并将上面创建的wakeFds数组传入。
int result = pipe(wakeFds);
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
//将管道的读端描述符赋值给mWakeReadPipeFd
mWakeReadPipeFd = wakeFds[0];
//将管道的写端描述符赋值给mWakeWritePipeFd
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.
//创建epoll专用的文件描述符,EPOLL_SIZE_HINT表示当前描述符可以监控的最大文件描述数
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;
/**通过epoll_ctl函数来告诉mEpollFd需要监控mWakeReadPipeFd文件描述符的EPOLLIN事件,当管道中有内容可读时就唤醒当前正在等待的线程。*/
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);
}
通过创建pipe管道的方式来对消息进行读写操作。管道的本质是一个伪文件,在管道的两端,分别是读端文件描述符和写段文件描述符,管道的读取顺序是先进先出。
- android6.0之后
Looper::Looper(bool allowNonCallbacks) :
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
//eventfd函数是linux2.6.27之后提供的新的系统调用,用来实现进程和线程之间的通信
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();
}
通过调用eventfd函数,获取eventfd所对应的文件描述符,eventfd函数是在Linux2.6.27版本才开始提供的一种新的函数,eventfd包含一个由内核维护的64位无符号整形计数器,创建eventfd时会返回一个文件描述符,线程可以用过这个文件描述符进行read/write操作。eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)表示创建一个非阻塞的eventfd或者在线程fork后exec其他程序时会自动关闭这个文件描述符。
下面我们来看下该函数中的rebuildEpollLocked函数。
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;
// 清空,把未使用的数据区域进行置0操作
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd;
/**通过epoll_ctl函数来告诉mEpollFd需要监控mWakeEventFd文件描述符的EPOLLIN事件,当管道中有内容可读时就唤醒当前正在等待的线程。*/
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));
// 这里主要处理的是Input事件,如键盘、传感器输入,基本上是由系统负责
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));
}
}
}
无论是pipe的实现方式,还是eventfd的实现方式,都是利用Linux中的epoll机制来处理线程的等待,唤醒操作。一个线程通过读端文件描述符读取管道中的信息,如果管道中没有数据,这个线程就会进入等待状态。而另外一个线程就可以在这个时间使用写端文件描述符对管道进行写操作,此时如果线程处于等待状态,写入数据之后就会唤醒当前正在等待的线程。这个等待,唤醒的操作就由Linux中的epoll机制来实现。epoll机制是为了同时监听多个文件描述符的IO读写事件而设计的,它是一个多路复用的IO接口,它类似于Linux中的select机制,但是它是select的增强版。如果一个epoll实例监听了大量的文件描述符的IO读写事件,但是只有少量的文件描述符是活跃的,也就是说只有少量的文件描述符发生了IO读写事件,那么这个epoll实例就可以显著的减少CPU的使用率,从而提高系统的并发处理能力。
至此,整个消息队列的创建过程就已经结束了,下面我们来分析线程消息的循环过程。
2.消息队列的循环过程
线程消息的循环主要是由Java层的Looper类的loop方法来执行的。当我们调用Looper.loop()的时候,上面所创建的消息队列就开始进入到一个消息循环中了。
1.调用Java层的Looper.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;
//此处省略无关代码
......
//开启死循环进行消息处理
for (;;) {
//从消息队列中取出消息
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
//此处处理消息
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
throw exception;
}
//处理完成之后回收当前消息
msg.recycleUnchecked();
}
}
此处开启死循环并不会造成线程阻塞,如果消息队列中没有消息需要处理,那么线程就会进入睡眠等待状态,直到有新的消息需要处理,后面我们会具体分析消息的处理过程。
2.MessageQueue.next
Message next() {
/**当mPtr等于0的时候,也就是并没有指向一个NativeMessageQueue的地址值,此时可能因为应用程序被重启,
从而导致Looper退出并重置,那么当前消息可能已经被处理或者已经退出。*/
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) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//如果队列头部是SyncBarrier,则直接跳过后面的同步Message,直奔下一个异步消息。SyncBarrier在ViewRootImpl执行Traversals相关的代码时有被设置。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
/**下一个消息的处理时间还没到,这时需要我们计算线程需要睡眠的时间,也就是下个消息需 要在什么时间去处理*/
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 下一个消息需要处理
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;
}
}
//省略空闲消息处理流程
......
/**因为在空闲消息处理器处理空闲消息的时候,可能会有新的消息加入到队列中,所以此时不能让线程进入睡 眠状态,需要再重新查看一下消息队列。*/
nextPollTimeoutMillis = 0;
}
}
上面的代码中我们可以看到pendingIdleHandlerCount这个变量,它是用来保存注册到空闲消息处理器的个数,后面我们会具体分析空闲消息处理器的流程。nextPollTimeoutMillis变量用来表示当前线程需要进入睡眠等待状态的时间。如果该变量的值等于0,当前线程就不会进入睡眠状态,如果该变量的值等于-1,那么就表示当前线程没有新的消息需要处理,可以进入到睡眠状态。我们通过nativePollOnce来检查当前线程中是否有新的消息需要处理。如果该方法里面没有阻塞线程,那么代码就会继续执行,那么就表示当前mMessages值不为空。每次进行循环的时候都会先去判断一下nextPollTimeoutMillis是否不等于0,也就是指这次循环当前线程是否会进入到等待状态,如果线程会进入到等待状态,那么此时我们就调用Binder类的静态成员函数flushPendingCommands来处理那些正在等待处理的Builder进程间通信请求。
3.MessageQueue.nativePollOnce
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
前面已经说过Java层的MessageQueue和C++层的NativeMessageQueue是通过mPtr来进行绑定的,这里就通过ptr地址值来取到NativeMessageQueue对象,然后再调用这个对象的pollOnce函数。
4.NativeMessageQueue.pollOnce
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
}
通过mLooper调用C++层的Looper对象的pollOnce函数,来检查当前线程是否有新的消息需要处理。
5.Looper.pollOnce
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
调用c++层的Looper.h中的内联函数pollOnce,避免了频繁调用函数对栈内存重复开辟所带来的消耗。
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
//先对fd对应的Response进行处理
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
//获取返回值,先处理没有CallBack的response事件
int ident = response.request.ident;
if (ident >= 0) {
//如果返回值大于等于0,那么就表示没有返回值,因为POLL_CALLBACK的值为-2
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;
}
}
//如果result的值不等于0,那么就表示当前线程有消息需要处理
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;
}
//在for循环中不断调用pollInner方法,获取返回值,
result = pollInner(timeoutMillis);
}
}
Looper.pollOnce函数中先去处理没有没有CallBack的Response事件,然后通过pollInner函数来取值,如果result的值不等于0。那么就表示当前线程有消息需要处理,就直接返回当前值。
6.Looper.pollInner
int Looper::pollInner(int timeoutMillis) {
// 根据下一条消息来设置超时时间
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;
//即将闲置
mPolling = true;
//设置最大数量的文件描述符来检索事件,最大数为16
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//重点,在下面详细分析
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//不是空闲消息
mPolling = false;
// 加锁,因为在Native Message的处理和添加逻辑上需要同步
mLock.lock();
// 如果需要就重建epoll
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
//通过此方法进行重建
rebuildEpollLocked();
goto Done;
}
// Check for poll error.
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
//epoll事件个数小于0,设置结果为错误,直接跳转
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
//epoll事件个数等于0,发生超时,直接跳转
result = POLL_TIMEOUT;
goto Done;
}
// 通过for循环来检查哪一个文件描述符发生了IO读写事件
for (int i = 0; i < eventCount; i++) {
//获取文件描述符
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
//如果文件描述符是与当前线程所关联的一个管道的读端文件描述符mWakeEventFd,并且所发生的IO读写事件为EPOLLIN,那么此时就说明其他线程与当前线程所关联的一个管段写入了一个新的数据。
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;
// 处理request,生成对应的response对象,push到响应数组
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// 调用等待的回调消息.
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();
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
this, handler.get(), message.what);
#endif
//调用handleMessage回调
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;
#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;
}
}
return result;
}
在创建消息队列的时候,我们创建了epoll实例,并将它的文件描述符保存在mEpollFd中,同时还将一个管道的读端文件描述符保存在它的里面。用来监听这个管道的IO写事件。在pollIner函数中我们通过epoll_wait函数来监听注册在前面的所创建的epoll实例中的mEpollFd的IO读写事件。如果这些文件描述符都没有发生IO读写事件,那么当前线程就会在epoll_wait中进入睡眠等待状态。
总结一下上面的流程:
1、先调用epoll_wait(),这是阻塞方法,用于等待事件发生或者超时。
2、对于epoll_wait()返回,当且仅当以下3种情况出现
- POLL_ERROR:发生错误,直接跳转Done
- POLL_TIMEOUT:发生超时,直接跳转到Done
- 检测到管道有事情发生,则再根据情况做相应处理:
- 如果检测到管道产生事件,则直接读取管道的数据
- 如果是其他事件,则处理request,生成对应的response对象,push到response数组
3、进入Done标记位的代码:
- 先处理Native的Message,调用Native的Handler来处理该Message
- 再处理Resposne数组,POLL_CALLBACK类型的事件
7.Looper.awoken
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ awoken", this);
#endif
uint64_t counter;
TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
在awoken函数中,通过read方法不断的从管道中读取数据,清空管道中的内容,这样在下一次循环的过程中,如果管道中没有数据,也就是说当前线程没有新的消息需要处理,那么它就会在第6步中的epoll_wait函数中进入睡眠等待状态,直到其他线程有新的消息写入进来。
下面我们对消息队列的循环过程进行一下总结:
1.消息的死循环并不会导致线程死锁,如果没有新的消息需要处理,那么线程就会在pollInner函数中发生睡眠等待状态。直到有新的消息需要处理,那么才会继续执行。
2.即使线程中没有新的消息需要处理,那么线程也不会立即进入睡眠状态,它会先去执行空闲消息处理器中等待的空闲消息。
3.消息处理的过程中,对于大部分C++层的代码都调用内联函数,避免频繁的创建栈空间,节省内存的消耗。
3.消息的发送过程
消息的发送过程主要使用Android中的Handler类。Handler类中包含一个sendMessage函数,调用这个函数就可以向消息队列中发送一条消息。下面我们继续分析消息的发送过程。
1.Handler.sendMessage
public class Handler{
//发送消息
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
//发送消息,并标明当前消息的延迟处理时间,delayMillis表示相对于当前时间的处理时间
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//发送消息,并标明当前消息需要处理的准确时间
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;
}
//调用enqueueMessage
return enqueueMessage(queue, msg, uptimeMillis);
}
//调用Handler的私有方法enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//设置msg.target为当前的Handler,后面处理消息的时候会用到这个target
msg.target = this;
//判断是否是异步消息
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用MessageQueue的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
}
2.MessageQueue.enqueMessage
public class MessageQueue{
.....
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//判断当前MessageQueue是否已经退出
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
//回收当前消息
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循环来确定当前消息需要插入的位置,按照时间进行排列
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;
}
......
}
插入消息可以分为四种情况
1.目标消息队列是一个空队列,也就是p=null;
2.插入的消息处理时间为0, msg.when = 0;
3.插入的消息处理时间小于当前消息队列中头部消息的处理时间,when<p.when;
4.插入的消息处理时间大于等于消息队列中头部消息的处理时间。
对于前面三种情况,新插入进来的消息都会放到当前消息队列的头部。第四种情况需要根据插入进来的消息的处理时间来进行排序,如果时间相同,那么先插入进来的消息放在前面,优先处理。
如果新插入进来的消息保存在头部,并且它的处理时间为0,那么就需要唤醒当前线程,进行处理。是否需要唤醒当前线程由变量needWake来决定。当前线程此时是否在睡眠状态由变量mBlocked来决定,我们可以根据这两个变量来决定有新的消息进来时是否唤醒当前线程。
3.MessageQueue.nativeWake
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}
根据参数ptr获取对应的NativeMessageQueue对象,并调用它的wake函数
4.NativeMessageQueue.wake
void NativeMessageQueue::wake() {
mLooper->wake();
}
调用对应的C++层的Looper对象的wake函数
5.Looper.wake
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
mWakeEventFd, strerror(errno));
}
}
}
调用write函数向管道中写入数据,这个时候目标线程就会因为发生了IO写事件而被唤醒。与2.6中的for循环遍历IO事件相呼应。
4.消息的处理过程
通过2.6,我们知道如果当前线程没有消息需要处理,那么就会进入睡眠等待状态。如果有新的消息需要处理,它首先会在Looper.pollInner中被唤醒,并沿着之前的调用路径一直返回到Java层的Looper.loop中。
1.Looper.loop
public class Looper{
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;
//清空远程线程的连接,确保使用使用本地线程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
boolean slowDeliveryDetected = false;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
//此处通过msg.target获取handler,并调用handler的dispatchMessage方法
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
}
2.Handler.dispatchMessage
public class handler{
......
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
......
}
当前方法的执行顺序为:
1.如果当前消息在发送的时候指定了一个回调接口,那么就调用handleCallback函数来执行
2.如果1不满足,那么要判断负责分发消息的Handler的mCallBack是否指向了一个回调接口,如果有,那就调用这个回调接口的handleMessage函数来处理这个消息。
3.如果2不满足,则直接调用handleMessage来处理消息。
6.Handler.handleMessage
/**
*子类必须实现该方法
*/
public void handleMessage(@NonNull Message msg) {
}
handleMessage是一个空的实现方法。一般情况下,我们使用子类来发送消息,这个子类重写了父类的handleMessage方法,同时我们将消息交给子类的handleMessage来处理。
参考文献:
《Android系统源代码情景分析》
《计算机操作系统》
《深入理解Android卷 I》
多线程通信总结列表
我是Android大师哥。成功并不是你拥有了多少,而是你帮助了多少人,又有多少人因你而感动。 谢谢大家,我们下期再见。