几个月前用安卓写代码时总结的一些东西,现在发出来充充数,以后要多写写博客了。
本文主要是针对Android消息分发过程中的几个要点:
messageQueue.next()
函数的具体过程
一个新的ActivityThread的创立,标志着一个新的应用被打开,而在安卓系统中,所有的应用都是以Activity的形式表示,桌面是一个Activity,导航栏是一个Activity,新打开的应用也是一个Activity。
每个应用都有一个自己的进程空间,应用进程通过vegote进行fork创建并启动我们指定的MainActivity,然后应用进程的主线程开始执行自己的main函数,初始化looper、messageQueue,并绑定Handler,然后进入loop循环开始等待消息的传入并处理。
根据不同的消息,主线程可能会通过Handler的不同处理,通知不同的Activity进入不同的状态,或者终结一个Activity的生命周期,或者仅仅只是将点击事件分发给不同的view让其自行执行回调。
至少从主线程循环的角度看来,安卓应用是一种消息驱动的应用模型。
其中Message mMessages记录的就是一条消息链表,而Looper的loop函数实质就是从MessageQueue中取出消息然后进行处理的过程。
另外在MessageQueue中还有几个native函数,这就说明MessageQueue会通过JNI技术调用到底层代码。mMessages域记录着消息队列中所有Java层的实质消息。当然,记录的只是Java层的消息,不包括C++层的。
MessageQueue的示意图如下:
MessageQueue.next()函数中中调用的nativePollOnce()调用到了C++层,这个函数起到了阻塞线程的作用,保证消息循环不会在无消息处理时一直在那里“傻转”,而通过native层的Looper实现可以很好的看到nativePollOnce函数是如何被阻塞,又是在何时被何种方式唤醒的。
C++层的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);
// 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));
}
}
}
在C++层中,可以看到在构造Looper对象时,通过eventfd函数创建了一个文件描述符,而eventfd创建的文件描述符最主要的作用就是信号通知,相比起之前版本消息通知使用的pipe机制,eventfd的开销更低且使用更少的文件描述符即可实现。
之后Looper通过epoll机制对eventfd的读写操作进行监控,这样就使得上层的loop循环会在MessageQueue.next()时阻塞在nativePollOnce函数上,在收到其他线程发来的消息之后被唤醒进行消息的处理。
每当我们向消息队列发送事件时,最终会间接向eventfd文件描述符上进行写操作,于是epoll通过监听获取到了这个事件,epoll_wait()在等到事件后,随即进行相应的事件处理。这就是消息循环阻塞并处理的大体流程。
以下是Loop函数的从上到下的流程图:
pollInner()调用epoll_wait()时传入的timeoutMillis参数,其实来自于前文所说的MessageQueue的next()函数里的nextPollTimeoutMillis,next()函数里在以下3种情况下,会给nextPollTimeoutMillis赋不同的值:
不管epoll_wait()的超时阀值被设置成什么,只要程序从epoll_wait()中返回,系统就会尝试处理等到的epoll事件。目前我们的主要关心点是事件机制,所以主要讨论当fd == mWakeEventFd时的情况,此时会调用一下awoken()函数。
之后,会挨个调用handler的handleMessage函数去处理每一个native层发过来的消息,并依次调用每个response的回调函数去处理事件。
而在这之后将会经由native层传入上方的java层,将消息交给java层的Looper和handler进行处理和分发。
C++层的Looper及这个层次的消息链表,再加上对应其他fd的Request和Response,可以形成下面这张示意图:
在收到其他进程发来的消息的时候,首先接到消息的是和其对应的拥有其Binder的线程,收到消息的线程将消息通过Handler发给主线程的消息队列,主线程再通过不同的接收Handler进行消息的处理。
比如点击事件的传递,其传播模型如上,消息的分发从上到下的逻辑也大致如此。
在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联,可以参考一下代码,在ActvityThread中,也就是ViewRootImpl是DecorView的父元素,但是ViewRootImpl并不是View。ViewRootImpl接收native层的消息,并向DecorView分发事件
这里的事件不仅仅包括MotionEvent,还有KeyEvent。我们知道View的时间分发顺序为Activity——>Window——>View,那么Activity的事件来源在哪里呢?这是个需要思考的问题,答案和ViewRootImpl有很大的关系。
首先,事件的根本来源来自于Native层的嵌入式硬件,然后会经过InputEventReceiver接受事件,然后交给ViewRootImpl,将事件传递给DecorView,DecorView再交给PhoneWindow,PhoneWindow再交给Activity。