Android应用程序消息处理机制分析_第1页
Android应用程序消息处理机制分析_第2页
Android应用程序消息处理机制分析_第3页
Android应用程序消息处理机制分析_第4页
Android应用程序消息处理机制分析_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Android 应用程序消息处理机制分析Android 像 Windows、MiniGui 等大多数系统一样都是通过消息来驱动的,特殊的是 Android 为每一个应用程序维护一个消息队列和消息循环,应用程序的主线程不断地从这个消息队列中获取消息,然后对这些消息进行处理,这样就实现了通过消息来驱动执行应用程序。Android 应用程序都是由 Activity 组成的,在我们点击主界面的应用程序图标后,Launch 程序一般情况下会通知 ActivityManagerService 向 zygote 发出请求,为应用程序创建一个新的进程并启动一个默认的 Activity。而应用程序的进程入口是 ActivityThread.main,我们可以理解为应用程序框架层为应用程序创建进程后,就把 ActivityThread 类加载进来,然后执行它的 main 函数。本文就以 ActivityManagerService 通知应用程序加载 MainActivity 为例,分析Android 应用程序的消息处理机制(Looper、Handler) 。这个过程,Launch 通过 ActivityManagerService 给 ActivityThread 发送请求,请求启动 MainActivity,这里 ActivityManagerService 只是把请求封装成一个消息,然后通过 quequeOrSendMessage 把消息放到应用程序的消息队列中,并没有等待请求操作完成就返回了。应用程序发现消息队列有消息时,通过handleMessage 处理这个消息,即调用 handleLaunchActivity 来执行实际的加载 MainAcitivy 类的操作。Launch通过ActivityManagerService 发 送 请msg放 到 应 用 程 序 的 消 息 队 列handleMessage处 理 消 息了解 Android 应用程序的消息处理过程后,就开始分析它的实现原理吧。与 Windows 应用程序的消息处理过程一样,Android 应用程序的消息处理机制也是由消息循环、消息发送和消息处理这三个部分组成的。重要的概念(类)首先大概了解几个重要的概念(类):Message:消息,可以理解为线程间通信的数据单元。MessageQueue:消息队列,用来存储一些待分发的 Message,内部实现了一个 Message 链表结构,一般按照先进先出原则执行(当然也有优先级或是时间排序等) 。Handler:是 Message 的主要处理者,负责将 message 添加到消息队列中以及对消息队列的 Message 进行处理(即分发消息) 。Looper:循环器,帮助一个线程维护一个消息队列,循环取出 MessageQueue中的 Message,并交给相应的 Handler 处理。线程:UI thread 通常就是 main thread,而 Android 启动程序时会替它建立一个 MessageQueue。每一个线程里可以含有一个 Looper 对象以及一个MessageQueue 数据结构。在应用程序里,可以定义 Handler 的子类来接收Looper 所发送的消息。下面我们开始分析应用程序消息处理机制的三个过程:1. 消息循环在消息处理机制中,消息都是存放在一个消息队列中的,而应用程序主线程就会围绕着这个消息队列进入一个无线循环(Looper) ,直到程序退出。如果队列中有消息,应用程序主线程就会把它取出来,并分发给相应的 Handler 处理;如果队列中没有消息,应用程序主线程就会进入空闲等待状态,等待着下一个消息的到来。1.1Looper 的创建(Java 层)我们先来看一下 Android 应用程序主线程是怎样进入消息循环的,前面讨论过在应用程序启动时,会在进程中加载 ActivityThread 类,并执行这个类的main 函数,而应用程序的消息循环就是在这个 main 中实现的,它定义在frameworks/base/core/java/android/app/ActivityThread.java 文件中:1. public final class ActivityThread 2. . 3. final H mH = new H();4. . 5. public static void main(String args) 6. . 7. Looper.prepareMainLooper(); 8. . 9. ActivityThread thread = new ActivityThread(); 10. thread.attach(false); 11. . 12. Looper.loop(); 13. . 14. 15. 这个函数做了两件事情,一是在主线程中创建了一个 ActivityThread 实例,在创建这个实例时,同时也会创建其成员变量,如 mH,它是 H 类型的,继承于 handler;二是通过 Looper 类使主线程进入消息循环中,这里我们只关注后者。首先看 Looper.prepareMainLooper 函数的实现,这是一个静态成员函数,定义在Frameworks/base/core/java/android/os/Looper.java 文件中:1. public class Looper 2. . 3. static final ThreadLocal sThreadLocal = new ThreadLocal (); 4. final MessageQueue mQueue; 5. . 6. public static final void prepare() 7. if (sThreadLocal.get() != null) 8. throw new RuntimeException(“Only one Looper may be created per thread“); 9. 10. sThreadLocal.set(new Looper(); 11. 12. public static final void prepareMainLooper() 13. prepare(); 14. setMainLooper(myLooper(); 15. myLooper().mQueue.mQuitAllowed = false;16. 17. private synchronized static void setMainLooper(Looper looper) 18. mMainLooper = looper; 19. 20. public static final Looper myLooper() 21. return (Looper)sThreadLocal.get(); 22. 23. private Looper() 24. mQueue = new MessageQueue(); 25. mRun = true; 26. mThread = Thread.currentThread(); 27. 28. . 29. 其实 prepareMainLooper 所做的事情就是在线程中创建一个 Looper 对象,这个 Looper 对象存放在 sThreadLocal 成员变量中,sThreadLocal 的类型是ThreadLocal,表示这是一个线程局部变量,而它又是 static、final 型的,即保证每一个调用了 prepare 函数的线程里都有一个独立的 Looper 对象。在创建Looper 对象时,在其构造函数中可以知道,它创建了一个消息队列MessageQueue,并保存在 mQueue 成员变量中,而往后的消息就是存放在这个队列中的。消息队列是消息处理机制的重要组件,所以我们看下它的创建过程,即构造函数的实现,实现frameworks/base/core/java/android/os/MessageQueue.java 中:1. public class MessageQueue 2. . 3. private int mPtr; / used by native code 4. private native void nativeInit(); 5. 6. MessageQueue() 7. nativeInit(); 8. 9. . 10. MessageQueue 的初始化工作都交给了 JNI 方法 nativeInit 来实现了,这个JNI 方法定义在Frameworks/base/core/jni/android_os_MessageQueue.cpp 中:1. static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) 2. NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); 3. . 4. android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue); 5. 6. NativeMessageQueue:NativeMessageQueue() 7. mLooper = Looper:getForThread(); 8. if (mLooper = NULL) 9. mLooper = new Looper(false); 10. Looper:setForThread(mLooper); 11. 12. 13. static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, 14. NativeMessageQueue* nativeMessageQueue) 15. env-SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, 16. reinterpret_cast(nativeMessageQueue); 17. 18. . 19. #define FIND_CLASS(var, className) 20. var = env-FindClass(className); 21. LOG_FATAL_IF(! var, “Unable to find class “ className);22. #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) 23. var = env-GetFieldID(clazz, fieldName, fieldDescriptor); 24. LOG_FATAL_IF(! var, “Unable to find field “ fieldName);25. intregister_android_os_MessageQueue(JNIEnv* env) 26. . 27. jclassclazz;28. FIND_CLASS(clazz, “android/os/MessageQueue“);29. GET_FIELD_ID(gMessageQueueClassInfo.mPtr, clazz,30. “mPtr“, “I“);31. return 0;32. 在 andoroid_os_MessageQueue_nativeInit 函数里,JNI 层也相应地创建了一个消息队列 NativeMessageQueue,NativeMessageQueue 类主要在内部创建了一个 Looper 对象,而这个 Looper 对象是实现在 C+层的,它与之前说过的Java 层的 Looper 是不一样的,不过它们是对应的,在进一步分析消息循环过程时,我们就可以清楚地了解它们之间的关系了。先来分析 android_os_MessageQueue_setNativeMessageQueue 函数吧,在创建了本地消息队列 NativeMessageQueue 后,通过调用android_os_MessageQueue_setNativeMessageQueue 把本地消息队列保存在Java 层的 MessageQueue 对象中的 mPtr 成员变量里。函数所传的参数MessageQueueObj 是 Java 层创建的消息队列, env 可以理解成:通过它我们可以很方便地进行 JNI 层与 Java 层间的交互。在注册 JNI 方法时,即在 register_android_os_MessageQueue 函数中,就把 Java 层的 MessageQueue 对象中成员变量 mPtr 与 JNI 层gMessageQueueClassInfo.mPtr 关联起来。也即gMessageQueueClassInfo.mPtr 可以表示为 MessageQueue 对象中成员变量mPtr 的偏移量,通过这个偏移量就可以把 JNI 层的本地消息队列NativeMessageQueue 保存在 Java 层 MessageQueue.mPtr 中,往后我们就可以在调用 Java 层的消息队列的其它成员函数而进入 JNI 层时,很方便地找到 JNI 层与之对应的消息队列了。我们再回到 NativeMessageQueue 的构造函数,看看 C+层的 Looper 对象的创建过程,Looper 类实现在 frameworks/base/libs/utils/Looper.cpp 中:1. Looper:Looper(bool allowNonCallbacks) : 2. mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),3. mResponseIndex(0), mNextMessageUptime(LLONG_MAX) 4. int wakeFds2; 5. int result = pipe(wakeFds); 6. . 7. mWakeReadPipeFd = wakeFds0; 8. mWakeWritePipeFd = wakeFds1; 9. . 10. #ifdef LOOPER_USES_EPOLL 11. / Allocate the epoll instance and register the wake pipe. 12. mEpollFd = epoll_create(EPOLL_SIZE_HINT); 13. . 14. struct epoll_event eventItem; 15. memset( / zero out unused members of data field union 16. eventItem.events = EPOLLIN; 17. eventItem.data.fd = mWakeReadPipeFd; 18. result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, 19. . 20. #else 21. . 22. #endif 23. . 24. 这个构造函数所做的事情非常重要,首先通过 pipe 创建了一个管道,接触过 pipe 应该都知道,管道是一个文件,在其两端分别是两个打开的文件描述符,它们都是对应于同一个文件,其中一个用来读,另一个用来写。一般是一个线程通过读文件描述符来读取管道中的内容,如果管道没有内容时,线程就会进入等待状态;另一个线程通过写文件描述符向管道写入内容,此时如果读的一端有线程正在等待读取管道中的内容,那么读端的线程就会被唤醒。在此线程的等待与唤醒中用到了 Linux 系统的 epoll 机制,epoll 机制可以处理大批量的句柄,是 Linux 下多路复用 IO 接口

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论