朝闻道 夕死可矣

2011-10-15 17:30:12

 


2011-10-15 17:30:12

 


它主要就是在内部创建了一个Looper对象,注意,这个Looper对象是实现在JNI层的,它与上面Java层中的Looper是不一样的,不过它们是对应的,

下面我们进一步分析消息循环的过程的时候,读者就会清楚地了解到它们之间的关系。
这个Looper的创建过程也很重要,不过我们暂时放一放,先分析完 android_os_MessageQueue_nativeInit 函数的执行,


它创建了本地消息队列NativeMessageQueue对象之后,接着调用android_os_MessageQueue_setNativeMessageQueue函数来把这个消息队列对象保存在前面我们在


Java层中创建的MessageQueue对象的mPtr成员变量里面:

 

view plaincopy to clipboardprint?
01.static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, 
02.        NativeMessageQueue* nativeMessageQueue) { 
03.    env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, 
04.             reinterpret_cast<jint>(nativeMessageQueue)); 
05.} 
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
        NativeMessageQueue* nativeMessageQueue) {
    env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
             reinterpret_cast<jint>(nativeMessageQueue));
}        这里传进来的参数messageQueueObj即为我们前面在Java层创建的消息队列对象,而gMessageQueueClassInfo.mPtr即表示在Java类MessageQueue中,

其成员变量mPtr的偏移量,通过这个偏移量,就可以把这个本地消息队列对象natvieMessageQueue保存在Java层创建的消息队列对象的mPtr成员变量中

,这是为了后续我们调用Java层的消息队列对象的其它成员函数进入到JNI层时,能够方便地找回它在JNI层所对应的消息队列对象。

    我们再回到NativeMessageQueue的构造函数中,看看JNI层的Looper对象的创建过程,即看看它的构造函数是如何实现的,
   
    这个Looper类实现在frameworks/base/libs/utils/Looper.cpp文件中:

 

view plaincopy to clipboardprint?
01.Looper::Looper(bool allowNonCallbacks) : 
02.    mAllowNonCallbacks(allowNonCallbacks), 
03.    mResponseIndex(0) { 
04.    int wakeFds[2]; 
05.    int result = pipe(wakeFds); 
06.    ...... 
07. 
08.    mWakeReadPipeFd = wakeFds[0]; 
09.    mWakeWritePipeFd = wakeFds[1]; 
10. 
11.    ...... 
12. 
13.#ifdef LOOPER_USES_EPOLL  
14.    // Allocate the epoll instance and register the wake pipe.  
15.    mEpollFd = epoll_create(EPOLL_SIZE_HINT); 
16.    ...... 
17. 
18.    struct epoll_event eventItem; 
19.    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union  
20.    eventItem.events = EPOLLIN; 
21.    eventItem.data.fd = mWakeReadPipeFd; 
22.    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); 
23.    ...... 
24.#else  
25.    ...... 
26.#endif  
27. 
28.    ...... 
29.} 
Looper::Looper(bool allowNonCallbacks) :
 mAllowNonCallbacks(allowNonCallbacks),
 mResponseIndex(0) {
 int wakeFds[2];
 int result = pipe(wakeFds);
 ......

 mWakeReadPipeFd = wakeFds[0];
 mWakeWritePipeFd = wakeFds[1];

 ......

#ifdef LOOPER_USES_EPOLL
 // Allocate the epoll instance and register the wake pipe.
 mEpollFd = epoll_create(EPOLL_SIZE_HINT);
 ......

 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);
 ......
#else
 ......
#endif

 ......
}        这个构造函数做的事情非常重要,它跟我们后面要介绍的应用程序主线程在消息队列中没有消息时要进入等待状态以及当消息队列有消息时要把应用程序主线程唤醒的

这两个知识点息息相关。它主要就是通过pipe系统调用来创建了一个管道了:


view plaincopy to clipboardprint?
01.int wakeFds[2]; 
02.int result = pipe(wakeFds); 
03....... 
04. 
05.mWakeReadPipeFd = wakeFds[0]; 
06.mWakeWritePipeFd = wakeFds[1]; 
int wakeFds[2];
int result = pipe(wakeFds);
......

mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];      


 管道是Linux系统中的一种进程间通信机制,具体可以参考前面一篇文章Android学习启动篇推荐的一本书《Linux内核源代码情景分析》中的第6章--传统的Uinx进程间通信。
 
 简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,
 
 别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,
 
 而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。
 
 这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。 Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,
 
 是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
 
 但是这里我们其实只需要监控的IO接口只有mWakeReadPipeFd一个,即前面我们所创建的管道的读端,为什么还需要用到epoll呢?
 
 有点用牛刀来杀鸡的味道。其实不然,这个Looper类是非常强大的,它除了监控内部所创建的管道接口之外,还提供了addFd接口供外界面调用,
 
 外界可以通过这个接口把自己想要监控的IO事件一并加入到这个Looper对象中去,当所有这些被监控的IO接口上面有事件发生时,就会唤醒相应的线程来处理,
 
 不过这里我们只关心刚才所创建的管道的IO事件的发生。

 
 要使用Linux系统的epoll机制,首先要通过epoll_create来创建一个epoll专用的文件描述符:


view plaincopy to clipboardprint?
01.mEpollFd = epoll_create(EPOLL_SIZE_HINT); 
mEpollFd = epoll_create(EPOLL_SIZE_HINT);       传入的参数EPOLL_SIZE_HINT是在这个mEpollFd上能监控的最大文件描述符数。

       接着还要通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件:


view plaincopy to clipboardprint?
01.struct epoll_event eventItem; 
02.memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union  
03.eventItem.events = EPOLLIN; 
04.eventItem.data.fd = mWakeReadPipeFd; 
05.result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); 
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);       这里就是告诉mEpollFd,

它要监控mWakeReadPipeFd文件描述符的EPOLLIN事件,即当管道中有内容可读时,就唤醒当前正在等待管道中的内容的线程。

阅读更多
个人分类: 杂项
想对作者说点什么? 我来说一句

VBA批量获取照片拍照时间

2015年05月13日 40KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭