android消息队列模型,Android消息处理机制(二):循环和消息队列的建立

Android应用程序线程的消息队列是使用一个MessageQueue对象来描述的,它随着Looper类的建立而建立,即经过调用Looper类的静态成员函数prepareMainLooper或者prepare来建立,其中,prepareMainLooper用来为应用程序的主线程建立消息队列;而prepare用来为应用程序的其余子线程建立消息队列。java

注意:prepareMainLooper只能在应用程序主线程中调用。Android应用程序主线程是一个特殊的线程,由于只有它才能够执行与UI相关的操做,所以,又成为UI线程。其余子线程须要执行一些UI操做时,必须使用函数getMainLooper获得主线程的Looper对象,往主线程的消息队列中发送与UI操做相关的消息。android

在分析Android应用程序线程的消息队列的建立以前,咱们首先简单介绍一下Looper类和MessageQueue类的实现。c++

因为Android应用程序的消息处理机制不只能够在Java代码中使用,也能够在C++代码中使用,所以,Android系统在C++层中有一个相应的Looper类和NativeMessageQueue类。其中,Java层中的Looper类和MessageQueue类是经过C++层中的Looper类和NativeMessageQueue类来实现的,它们的关系如图。函数

a98328b87f4c48d3b44670f231eaa59a.gif

Looper、MessageQueue、Looper(Native)和NativeMessageQueue关系图oop

注意:Java层的消息处理机制和C++层的消息处理机制没有太大的联系,只是Java层消息处理机制使用了C++层消息处理机制的休眠/唤醒机制。如有时间,后面会讲解C++层消息处理机制。ui

Java层中的每个Looper对象内部都有一个类型为MessageQueue的成员变量mQueue,它只想一个MessageQueue对象;在C++层中,每个NativeMessageQueue对象内部都有一个类型为Looper的成员变量,它指向一个C++层的Looper对象。spa

Java层中的每个MessageQueue对象都有一个类型为int的成员变量mPtr,它保存了C++层中的一个NativeMessageQueue对象的地址值,这样咱们就能够将java层中的一个MessageQueue对象与C++层中的一个NativeMessageQueue对象关联起来。.net

Java层中的每个MessageQueue对象还有一个类型为Message的成员变量mMessages,它用来描述一个消息队列,咱们能够调用MessageQueue类的成员变量enqueueMessage来往里面添加一个消息。线程

C++层中的Looper对象有两个类型为int的成员变量mWakeReadPipeFd和mWakeWritePipeFd,它们分别用来描述一个管道的读端文件描述符和写端文件描述符。当一个线程的消息队列没有消息须要处理时,它就会在这个管道的读端文件描述符上进行睡眠等待,直到其余线程经过这个管道的写端文件描述符来唤醒它为止。code

当咱们调用调用Java层的Looper类的静态成员函数prepareMainLooper或者prepare来为一个线程建立一个消息队列时,Java层的Looper类就会在这个线程中建立一个Looper对象和一个MessageQueue对象。在建立Java层的MessageQueue对象的过程当中,又会调用它的成员函数nativeInit在C++层中建立一个NativeMessageQueue对象和一个Looper对象。在建立C++层的Looper对象是,又会建立一个管道,这个管道的读端文件描述符和写端文件描述符就保存在它的成员变量mWakeReadPipeFd和mWakeWritePipeFd中。

下面咱们对源码进行分析,首先看看时序图。

a98328b87f4c48d3b44670f231eaa59a.gif

循环和消息队列建立时序图

简单介绍一下Looper类中的

/*建立一个Looper对象(子线程)*/ public static void prepare() { prepare(true); } /*若是quitAllowed为true则表示该子线程能够销毁,通常 *为主线程以外的其余线程。 *若是quitAllowed为false则表示该线程为主线程 **/ private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } /*sThreadLocal保存线程中的Looper对象。*/ sThreadLocal.set(new Looper(quitAllowed)); } public static void prepareMainLooper() { /*建立一个Looper对象*/ prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } public static Looper myLooper() { turn sThreadLocal.get(); }

getMainLooper()用于获得主线程的Looper对象,咱们知道主线程是UI线程,不能本身退出。getLooper()用于获得其余子线程的Looper对象。注意到主线程和子线程建立的Looper对象,都被赋值给类型为sThreadLocal的静态变量sThreadLocal ,它具体有什么用,咱们后面讨论。子线程和主线程还有一个主要的区别,主线程的Looper对象被赋值给类的静态成员变量sMainLooper。下面开始从源码的角度分析循环和消息队列的建立过程。

frameworks\base\core\java\android\os\Looper.java

/*建立一个Looper对象(子线程)*/

public static void prepare() {

prepare(true);

}      Looper类的静态方法prepare()用来建立一个Looper对象,调用重载同名的静态方法prepare();下面咱们看一下重载的成员方法prepare()。

frameworks\base\core\java\android\os\Looper.java

private static void prepare(boolean quitAllowed) {

if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");

}

/*sThreadLocal保存线程中的Looper对象。*/

sThreadLocal.set(new Looper(quitAllowed));

}      重载函数,quitAllowed默认为true,从名字能够看出来就是消息循环是否能够退出,默认是可退出的,Main线程(UI线程)初始化消息循环时会调用prepareMainLooper,传进去的是false。感兴趣的能够本身查看。最后调用了Looper的构造方法,而且把建立的Looper类保存在ThreadLocal,

每一个线程能够初始化一个Looper。ThreadLocal后期咱们会单独研究。下面咱们看一下Looper类的构造方法。

frameworks\base\core\java\android\os\Looper.java

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}      Looper类的构造方法,首先建立一个消息循环队列,参数为quitAllowed,意义同上。调用Thread类的currentThread()方法,把当前线程存在Looper的成员变量mThread。下面咱们看一下MessageQueue类的构造方法。

frameworks\base\core\java\android\os\MessageQueue.java

MessageQueue(boolean quitAllowed) {

mQuitAllowed = quitAllowed;

mPtr = nativeInit();

}      MessageQueue类的构造方法作了两件事,第一件事是把Looper类传递过来的参数quitAllowed赋值给MessageQueue类的成员变量mQuitAllowed,变量ture为容许退出,false为不能退出。第二件事是调用JNI方法nativeInit(),并把返回值保存在MessageQueue类的成员变量mPtr中。前面咱们介绍到,mPtr保存的是Native层的NativeMessageQueue类的地址值。下面咱们看一下Native的JNI方法。

frameworks\base\core\jni\android_os_MessageQueue.cpp

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {

/*建立一个NativeMessageQueue对象,在内部建立一个C++层的Looper对象*/

NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();

if (!nativeMessageQueue) {

jniThrowRuntimeException(env, "Unable to allocate native queue");

return 0;

}

nativeMessageQueue->incStrong(env);/*增长引用计数*/

return reinterpret_cast(nativeMessageQueue);

}      从以上代码能够看出,native层的方法主要是建立了一个NativeMessageQueue对象,增长它的强引用计数,把它的地址值返回给调用对象。下面咱们看一下Native层的NativeMessageQueue构造方法。

frameworks\base\core\jni\android_os_MessageQueue.cpp

NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {

/*getForThread检查是否已经为当前线程建立一个c++层的Looper对象*/

mLooper = Looper::getForThread();

if (mLooper == NULL) {

mLooper = new Looper(false);

Looper::setForThread(mLooper);

}

}      NativeMessageQueue的构造方法,先调用native层Looper对象的成员方法getForThread(),检查是否已经为当前线程建立一个c++层的Looper对象。若是没有则建立Looper对象,并保存在NativeMessageQueue对象的成员变量mLooper中。下面看一下native层Looper类的构造方法。

system\core\libutils\Looper.cpp

Looper::Looper(bool allowNonCallbacks) :

mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),

mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {

int wakeFds[2];

int result = pipe(wakeFds);

mWakeReadPipeFd = wakeFds[0];

mWakeWritePipeFd = wakeFds[1];

......

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);

}      Native层的Looper使用了epoll。初始化了一个管道,用mWakeWritePipeFd与mWakeReadPipeFd分别保存了管道的写端与读端,并监听了读端的EPOLLIN事件。epoll机制,感兴趣的本身能够先了解一下,后面咱们会补充一下。

思考(之后的拓展方向): 一、注意下初始化列表的值,mAllowNonCallbacks的值为false。mAllowNonCallback是作什么的? 二、使用epoll仅为了监听mWakeReadPipeFd的事件?其实Native Looper不只能够监听这一个描述符, Looper还提供了addFd方法。handler机制会怎么处理这些?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值