Android应用程序消息处理机制LooperHandler分析.docx
《Android应用程序消息处理机制LooperHandler分析.docx》由会员分享,可在线阅读,更多相关《Android应用程序消息处理机制LooperHandler分析.docx(46页珍藏版)》请在冰豆网上搜索。
Android应用程序消息处理机制LooperHandler分析
Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行,本文将详细分析Android应用程序的消息处理机制。
前面我们学习Android应用程序中的Activity启动(Android应用程序启动过程源代码分析和Android应用程序内部启动Activity过程(startActivity)的源代码分析)、Service启动(Android系统在新进程中启动自定义服务过程(startService)的原理分析和Android应用程序绑定服务(bindService)的过程源代码分析)以及广播发送(Android应用程序发送广播(sendBroadcast)的过程分析)时,它们都有一个共同的特点,当ActivityManagerService需要与应用程序进行并互时,如加载Activity和Service、处理广播待,会通过Binder进程间通信机制来知会应用程序,应用程序接收到这个请求时,它不是马上就处理这个请求,而是将这个请求封装成一个消息,然后把这个消息放在应用程序的消息队列中去,然后再通过消息循环来处理这个消息。
这样做的好处就是消息的发送方只要把消息发送到应用程序的消息队列中去就行了,它可以马上返回去处理别的事情,而不需要等待消息的接收方去处理完这个消息才返回,这样就可以提高系统的并发性。
实质上,这就是一种异步处理机制。
这样说可能还是比较笼统,我们以Android应用程序启动过程源代码分析一文中所介绍的应用程序启动过程的一个片断来具体看看是如何这种消息处理机制的。
在这篇文章中,要启动的应用程序称为Activity,它的默认Activity是MainActivity,它是由Launcher来负责启动的,而Launcher又是通过ActivityManagerService来启动的,当ActivityManagerService为这个即将要启的应用程序准备好新的进程后,便通过一个Binder进程间通信过程来通知这个新的进程来加载MainActivity,如下图所示:
它对应Android应用程序启动过程中的Step30到Step35,有兴趣的读者可以回过头去参考Android应用程序启动过程源代码分析一文。
这里的Step30中的scheduleLaunchActivity是ActivityManagerService通过Binder进程间通信机制发送过来的请求,它请求应用程序中的ActivityThread执行Step34中的performLaunchActivity操作,即启动MainActivity的操作。
这里我们就可以看到,Step30的这个请求并没有等待Step34这个操作完成就返回了,它只是把这个请求封装成一个消息,然后通过Step31中的queueOrSendMessage操作把这个消息放到应用程序的消息队列中,然后就返回了。
应用程序发现消息队列中有消息时,就会通过Step32中的handleMessage操作来处理这个消息,即调用Step33中的handleLaunchActivity来执行实际的加载MainAcitivy类的操作。
了解Android应用程序的消息处理过程之后,我们就开始分样它的实现原理了。
与Windows应用程序的消息处理过程一样,Android应用程序的消息处理机制也是由消息循环、消息发送和消息处理这三个部分组成的,接下来,我们就详细描述这三个过程。
1.消息循环
在消息处理机制中,消息都是存放在一个消息队列中去,而应用程序的主线程就是围绕这个消息队列进入一个无限循环的,直到应用程序退出。
如果队列中有消息,应用程序的主线程就会把它取出来,并分发给相应的Handler进行处理;如果队列中没有消息,应用程序的主线程就会进入空闲等待状态,等待下一个消息的到来。
在Android应用程序中,这个消息循环过程是由Looper类来实现的,它定义在frameworks/base/core/java/android/os/Looper.java文件中,在分析这个类之前,我们先看一下Android应用程序主线程是如何进入到这个消息循环中去的。
在Android应用程序进程启动过程的源代码分析一文中,我们分析了Android应用程序进程的启动过程,Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的,我们来看看这个函数的实现,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:
viewplain
1.public final class ActivityThread {
2. ......
3.
4. public static final void main(String[] args) {
5. ......
6.
7. Looper.prepareMainLooper();
8.
9. ......
10.
11. ActivityThread thread = new ActivityThread();
12. thread.attach(false);
13.
14. ......
15.
16. Looper.loop();
17.
18. ......
19.
20. thread.detach();
21.
22. ......
23. }
24.}
这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中,这里我们只关注后者。
首先看Looper.prepareMainLooper函数的实现,这是一个静态成员函数,定义在frameworks/base/core/java/android/os/Looper.java文件中:
viewplain
1.public class Looper {
2. ......
3.
4. private static final ThreadLocal sThreadLocal = new ThreadLocal();
5.
6. final MessageQueue mQueue;
7.
8. ......
9.
10. /** Initialize the current thread as a looper.
11. * This gives you a chance to create handlers that then reference
12. * this looper, before actually starting the loop. Be sure to call
13. * {@link #loop()} after calling this method, and end it by calling
14. * {@link #quit()}.
15. */
16. public static final void prepare() {
17. if (sThreadLocal.get() !
= null) {
18. throw new RuntimeException("Only one Looper may be created per thread");
19. }
20. sThreadLocal.set(new Looper());
21. }
22.
23. /** Initialize the current thread as a looper, marking it as an application's main
24. * looper. The main looper for your application is created by the Android environment,
25. * so you should never need to call this function yourself.
26. * {@link #prepare()}
27. */
28.
29. public static final void prepareMainLooper() {
30. prepare();
31. setMainLooper(myLooper());
32. if (Process.supportsProcesses()) {
33. myLooper().mQueue.mQuitAllowed = false;
34. }
35. }
36.
37. private synchronized static void setMainLooper(Looper looper) {
38. mMainLooper = looper;
39. }
40.
41. /**
42. * Return the Looper object associated with the current thread. Returns
43. * null if the calling thread is not associated with a Looper.
44. */
45. public static final Looper myLooper() {
46. return (Looper)sThreadLocal.get();
47. }
48.
49. private Looper() {
50. mQueue = new MessageQueue();
51. mRun = true;
52. mThread = Thread.currentThread();
53. }
54.
55. ......
56.}
函数prepareMainLooper做的事情其实就是在线程中创建一个Looper对象,这个Looper对象是存放在sThreadLocal成员变量里面的,成员变量sThreadLocal的类型为ThreadLocal,表示这是一个线程局部变量,即保证每一个调用了prepareMainLooper函数的线程里面都有一个独立的Looper对象。
在线程是创建Looper对象的工作是由prepare函数来完成的,而在创建Looper对象的时候,会同时创建一个消息队列MessageQueue,保存在Looper的成员变量mQueue中,后续消息就是存放在这个队列中去。
消息队列在Android应用程序消息处理机制中最重要的组件,因此,我们看看它的创建过程,即它的构造函数的实现,实现frameworks/base/core/java/android/os/MessageQueue.java文件中:
viewplain
1.public class MessageQueue {
2. ......
3.
4. private int mPtr; // used by native code
5.
6. private native void nativeInit();
7.
8. MessageQueue() {
9. nativeInit();
10. }
11.
12. ......
13.}
它的初始化工作都交给JNI方法nativeInit来实现了,这个JNI方法定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:
viewplain
1.static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
2. NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
3. if (!
nativeMessageQueue) {
4. jniThrowRuntimeException(env, "Unable to allocate native queue");
5. return;
6. }
7.
8. android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
9.}
在JNI中,也相应地创建了一个消息队列NativeMessageQueue,NativeMessageQueue类也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的创建过程如下所示:
viewplain
1.NativeMessageQueue:
:
NativeMessageQueue() {
2. mLooper = Looper:
:
getForThread();
3. if (mLooper == NULL) {
4. mLooper = new Looper(false);
5. Looper:
:
setForThread(mLooper);
6. }
7.}
它主要就是在内部创建了一个Looper对象,注意,这个Looper对象是实现在JNI层的,它与上面Java层中的Looper是不一样的,不过它们是对应的,下面我们进一步分析消息循环的过程的时候,读者就会清楚地了解到它们之间的关系。
这个Looper的创建过程也很重要,不过我们暂时放一放,先分析完android_os_MessageQueue_nativeInit函数的执行,它创建了本地消息队列NativeMessageQueue对象之后,接着调用android_os_MessageQueue_setNativeMessageQueue函数来把这个消息队列对象保存在前面我们在Java层中创建的MessageQueue对象的mPtr成员变量里面:
viewplain
1.static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,
2. NativeMessageQueue* nativeMessageQueue) {
3. env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,
4. reinterpret_cast(nativeMessageQueue));
5.}
这里传进来的参数messageQueueObj即为我们前面在Java层创建的消息队列对象,而gMessageQueueClassInfo.mPtr即表示在Java类MessageQueue中,其成员变量mPtr的偏移量,通过这个偏移量,就可以把这个本地消息队列对象natvieMessageQueue保存在Java层创建的消息队列对象的mPtr成员变量中,这是为了后续我们调用Java层的消息队列对象的其它成员函数进入到JNI层时,能够方便地找回它在JNI层所对应的消息队列对象。
我们再回到NativeMessageQueue的构造函数中,看看JNI层的Looper对象的创建过程,即看看它的构造函数是如何实现的,这个Looper类实现在frameworks/base/libs/utils/Looper.cpp文件中:
viewplain
1.Looper:
:
Looper(bool allowNonCallbacks) :
2. mAllowNonCallbacks(allowNonCallbacks),
3. mResponseIndex(0) {
4. int wakeFds[2];
5. int result = pipe(wakeFds);
6. ......
7.
8. mWakeReadPipeFd = wakeFds[0];
9. 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.}
这个构造函数做的事情非常重要,它跟我们后面要介绍的应用程序主线程在消息队列中没有消息时要进入等待状态以及当消息队列有消息时要把应用程序主线程唤醒的这两个知识点息息相关。
它主要就是通过pipe系统调用来创建了一个管道了:
viewplain
1.int wakeFds[2];
2.int result = pipe(wakeFds);
3.......
4.
5.mWakeReadPipeFd = wakeFds[0];
6.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专用的文件描述符:
viewplain
1.mEpollFd = epoll_create(EPOLL_SIZE_HINT);
传入的参数EPOLL_SIZE_HINT是在这个mEpollFd上能监控的最大文件描述符数。
接着还要通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件:
viewplain
1.struct epoll_event eventItem;
2.memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
3.eventItem.events = EPOLLIN;
4.eventItem.data.fd = mWakeReadPipeFd;
5.result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
这里就是告诉mEpollFd,它要监控mWa