android应用消息处理机制分析之消息循环

来源:互联网 发布:sql create 字段唯一 编辑:程序博客网 时间:2024/05/30 04:54
前奏:这三篇消息处理机制,其实完全是根据老罗的博客来跟代码,在下对其仰慕许久,而一直无缘相见。尔后一个字一个字的敲下来,记录于有道笔记之上,当然其中的代码部分只能是复制源码中的了。之前去面试,或者从一开始面试就经常被问到诸如:你给我说说handler的机制....这些老生长谈的话题,加上本身确实没时间去深究源码,其次是确实是有点懒,所以回答的不过寥寥数语而已,之后并未认真细看熟记。此次下狠心,若是哪个不可一世的面试官再次提及,也许可以说个天花乱坠。

Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,
应用程序的主线程不断地从这个消息队例中获取消息(Looper),
然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行

Android应用程序的消息处理机制也是由消息循环、消息发送和消息处理这三个部分组成。

1、消息循环
      消息处理机制中、消息是存放在一个消息队列中。
而应用程序的主线程就是围绕这个消息队列进入一个无限循环的,
直到应用程序退出。如果队列中有消息,应用程序的主线程就会把它取出来,
并分发给相应的Handler进行处理;如果队列中没有消息,
应用程序的主线程就会进入空闲等待状态,等待下一个消息的到来。
在Android应用程序中,这个消息循环过程是由Looper类来实现的,
它定义在frameworks/base/core/java/android/os/Looper.java文件中,
在分析这个类之前,我们先看一下Android应用程序主线程是如何进入到这个消息循环中去的。
1.1、ActivityThread.java中的main方法
void main(String [] args){
......
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();
......
}
该方法做了两件事情:创建ActivityThread的实例、二是通过Looper类使主线程进入消息循环。
1.1.1、先看Looper.prepareMainLooper();
{
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
1.1.1、先分析prepare()
prepare(){
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建一个Looper对象,并保存在sThredLocal成员变量里面,类型是ThredLocal
//并且保证了每一个调用该方法的线程都有了一个独立的Looper对象。
sThreadLocal.set(new Looper()); 
}
1.1.1.1、接着是Looper的构造方法
Looper(){
mQueue = new MessageQueue(); //创建一个消息队列,保存在成员变量mQueue中
mRun = true;
mThread = Thread.currentThread(); //保存当前线程在成员变量mThread中
}
1.1.1.1、接着MessageQueue的构造方法
 MessageQueue() {
nativeInit();
}
接着查看nativeInit() //frameworks/base/core/jni/android_os_MessageQueue.cpp
1.1.1.1.1、static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
//创建了一个消息队列NativeMessageQueue
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (! nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return;
}
android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
}
再接着:
1.1.1.1.1.1、NativeMessageQueue::NativeMessageQueue() {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
主要作用是:内部创建一个Looper对象,注意,此对象是实现在JNI层,
与上层java中的Looper是不一样的。but,它们是对应的。
接着看看 mLooper = new Looper(false); 
Looper::Looper(bool allowNonCallbacks) :  
mAllowNonCallbacks(allowNonCallbacks),  
mResponseIndex(0) {
int wakeFds[2];  
int result = pipe(wakeFds);   //pipe系统,创建pipe  详见注解1
......  
mWakeReadPipeFd = wakeFds[0];  
mWakeWritePipeFd = wakeFds[1];  
......
// Allocate the epoll instance and register the wake pipe.  
//创建一个epoll专用的文件描述符
mEpollFd = epoll_create(EPOLL_SIZE_HINT);  
......
//通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件
//这里告诉mEpollFd ,它要监控mWakeReadPipeFd文件描述符的EPOLLIN事件
//即当管道中有内容可读时,就唤醒当前正在等待管道内容的线程
eventItem.events = EPOLLIN;  
eventItem.data.fd = mWakeReadPipeFd;  
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
1.1.1.1.1.2、android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue){
 NativeMessageQueue* nativeMessageQueue) {
//messageQueueObj java 层创建的消息队列对象
//gMessageQueueClassInfo.mPtr  
//对应java类MessageQueue中成员变量mPtr,为了方便找回它在JNI所对应的消息队列对象
env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
 reinterpret_cast<jint>(nativeMessageQueue));
}
至此:总结一下
C++层的这个Looper对象创建好了之后,就返回到JNI层的NativeMessageQueue的构造函数,
最后就返回到Java层的消息队列MessageQueue的创建过程,
这样,Java层的Looper对象就准备好了。有点复杂,我们先小结一下这一步都做了些什么事情:
   A. 在Java层,创建了一个Looper对象,这个Looper对象是用来进入消息循环的,它的内部有一个消息队列MessageQueue对象mQueue;
   B. 在JNI层,创建了一个NativeMessageQueue对象,这个NativeMessageQueue对象保存在Java层的消息队列对象mQueue的成员变量mPtr中;
   C. 在C++层,创建了一个Looper对象,保存在JNI层的NativeMessageQueue对象的成员变量mLooper中,
   这个对象的作用是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,
   而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息。


1.2、回到Looper,prepare之后就开始调用loop()进入循环了。
public static final void loop() {  
......
Looper me = myLooper();  
MessageQueue queue = me.mQueue;    
......    
while (true) {   //循环操作
Message msg = queue.next(); // might block   读取下一个消息
......    
if (msg != null) {  
if (msg.target == null) {   //target成员为null,则跳出循环
// No target is a magic identifier for the quit message.  
return;  
}    
......    
msg.target.dispatchMessage(msg);  //调用target的dispaMessage(msg),这个target即handler              
......    
msg.recycle();  
}  
}  
}  
接着查看MessageQueue的next(),注意在这个方法下,线程会进入等待状态。两种情况下回进入等待状态。
一是目前消息队列中没有消息,二是消息指定了执行的时间,目前没有到点,无法执行。
final Message next() {
for(;;){
......
//mPtr 即JNI层创建的NativeMessageQueue对象
//nextPollTimeoutMillis,表示要等待时间 ,默认初始值是0
nativePollOnce(mPtr, nextPollTimeoutMillis);
......
//返回后看看消息队列中是否有消息,若有,则当前时间大于消息中的执行时间
//那么就直接返回消息给Looper.loop消息处理,否则的话就要等待消息的执行时间
//这就是为什么我可以使用postDeley(msg,timeout)来指定了
final Message msg = mMessages;  
if (msg != null) {  
final long when = msg.when;  
if (now >= when) {  //大于执行时间,直接返回消息
mBlocked = false;  
mMessages = msg.next;  
msg.next = null;  
if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);  
return msg;  
} else {   //小于执行时间,则计算仍需等待多少时间,然后才返回
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);  
}  
} else {   、
//如果没有消息,则令nextPollTimeoutMillis = -1
//表示下次调用nativePollOnce()时,线程进入无限的空闲等待装填
nextPollTimeoutMillis = -1;   
//因为是循环调用,第一次的nextPollTimeoutMillis不是0,所以就无需进入等待状态,
//第二次之后才开始可以进入等待状态,不过在进入等待状态之前,
//如果应用程序注册了IdleHandler接口来处理一些事情,那么就会先执行这里IdleHandler,然后再进入等待状态。
 // If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount == 0) { //没有注册IdlerHandler ,continue到下一次循环
// No idle handlers to run.  Loop and wait some more.
mBlocked = true;
continue;
}

if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//若存在注册的IdlerHandler,则需要把注册在mIdleHandlers中的IdleHandler取出来,
//放到mPendingIdleHandlers数组中去。
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
//接下来就是执行这些注册了的IdleHandler了
 for (int i = 0; i < pendingIdleHandlerCount; i++) {
......
 }
 
//最后
// 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;
}
}
IdlerHandler的定义如下:
public static interface IdleHandler { 
//若返回值为false,则应用程序将会移除上面的IdlerHandler
//否则将一直维护着,直到下一次循环调用到空闲时,仍会执行IdlerHandler。
//MessageQueue提供了addIdleHandler和removeIdleHandler两注册和删除IdleHandler。
boolean queueIdle(); 
}
-------------------------------------------------------------
至此,java层中的MessageQueue中的next()已经分析完毕,接下来就是要分析
JNI层的nativePollOnce(mPtr, nextPollTimeoutMillis),看看它是如何获取到消息
并且在无消息的时候,进入到空闲状态。
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jint ptr, jint timeoutMillis) {
//通过传递进来的ptr,得到之前创建的在JNI层创建的NativeMessageQueue。
//注意,这个NativeMessageQueue是在我们创建java层的MessageQueue的时候创建的。
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(timeoutMillis);
}
接着看看nativeMessageQueue的pollOnce()
void NativeMessageQueue::pollOnce(int timeoutMillis) {
//这里转发给了Looper对象处理,这个对象是我们在JNI层创建NativeMessageQueue时创建的。
mLooper->pollOnce(timeoutMillis);
}
再接着
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {  
int result = 0;  
for (;;) {  
......    
if (result != 0) {  
......    
return result;  
}    
result = pollInner(timeoutMillis);   //主要核心
}  

继续:
int Looper::pollInner(int timeoutMillis) {  
......
//查看epoll专用文件描述符mEpollFd所监控的文件描述符是否有I/O事件发生
//它设置监控的超时时间为timeoutMillis
//在之前的Looper构造函数中我们设置了要监控mWakeReadPipeFd文件描述符的EPOLLIN事件。
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);  
若发生I/O事件或者监控超时,线程就从epoll_wait返回了,否则线程就会在epoll_wait函数中进入睡眠状态了。
返回后如果eventCount等于0,就说明是超时了
if (eventCount == 0) {  
......  
result = ALOOPER_POLL_TIMEOUT;  
goto Done;  
}  
若不等于0,则表明发生了监控事件 
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 {  
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);  
}  
} else {  
......  
}  
}
接着调用到awoken()把清空管道中的内容,以便下次再调用pollInner时,就知道有没有新的消息进来了。
}
查看awoken()
void Looper::awoken(){
......    
//实现比较简单,就是把内容读取出来
char buffer[16];  
ssize_t nRead;  
do {  
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));  
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));  
}
当其他线程向应用程序的消息队列发送消息时,会向这个管道写入心的内容
来通知应用程序主线程有新的消息需要处理啦。
注解:
1、简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,
这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,
一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,
这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,
写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。
这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。 
哇塞~又发现一个新东西,判断两个数的差操作,android源码的MessageQueue中的next()
在判断当前时间与消息的执行时间距离多少时,是这么写的。
 nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
 
 eg:
long when = 178888;
long now = 278888;
int reuslt = (int) Math.min(when - now, Integer.MAX_VALUE);
System.out.println("result = "+reuslt);
result = -100000
我终于明白力凯为什么在写代码的时候,构造一些初始值的时候,都喜欢使用self()来命名了。
原来都是C源码看多了。
-------------------------------------------------------------------
其实以上内容可以分为两个部分来理解。
1、创建MessageQueue、其中包括什么创建pipe ,使用epoll create一个专用描述符,
收集描述符mWakeReadPipeFd发生的监控事件。
2、回到主线程(ActivityThread中)执行Looper.loop()。
原创粉丝点击