前置知识1:
学习任何知识都是要有一定的前置条件的,比如你要先识字才能“看懂”我这篇文章…
在全面了解Android消息处理机制之前,我希望读者对该机制最好要有一定的了解,至少知道主要由哪些类实现了该机制。
如果你还能看得懂c/c++代码,精通Linux内核,对pipe、eventfd和IO多路复用epoll机制都有深入的了解,那么我劝你就别看了…
本文的侧重点主要在于消息机制中的阻塞和唤醒上,对于Java层面的组成关系会简单的带过,这部分的内容已经烂大街了,如果想对于Java层的源码逐行分析请看下面的源码分析(
点了之后先不要打我,我其实真的是准备自己写一篇的
目录
主要组成
Android系统中主要由MessageQueue、Looper和Handler来实现Android应用程序的消息处理机制的。
MessageQueue用来描述消息队列Looper用来创建消息队列,以及开启消息循环Handler用来发送和处理消息
创建过程
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
...省略部分代码...
//调用了Looper构造函数,创建了MessageQueue消息队列
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//开启消息循环
Looper.loop();
}
frameworks/base/core/java/android/os/Looper.java
public static void prepareMainLooper() {
prepare(false);
...省略部分代码...
}
private static void prepare(boolean quitAllowed) {
...省略部分代码...
//通过Looper构造函数创建对象,放入ThreadLocal里保证线程唯一
sThreadLocal.set(new Looper(quitAllowed));
}
//Looper的构造函数 内部调用了MessageQueue的构造函数
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
frameworks/base/core/java/android/os/MessageQueue.java
//用来保存nativeMessageQueue的指针对应的内存地址
private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//【重点1】
mPtr = nativeInit();
}
Android消息处理机制中三个实现类的其中两个就这么简简单单的被创建出来了。通过这个调用顺序我们可以了解到 ActivityThread构建了Looper对象,Looper对象构建了MessageQueue对象。

如果抛开MessageQueue构造函数中的nativeInit() 函数,我相信大部分Android开发者都能看懂上面的代码,到这里Java层主要组成部分的Looper对象和MessageQueue对象都创建都完成了。
这里为什么提“Java层主要组成部分”这个词汇,其实是因为我们的消息机制的组成不单单由Java层构成,还由C/C++层面的源码构成。就像我们Android系统一样
而C/C++这部分的入口就在这个nativeInit() 函数中。接下来我们就直接分析这个函数。这个函数从名字上可以看出是一个JNI调用。对应的是底层的C/C++代码。如果你看不懂C/C++代码也不要紧,先跟着看,我会最后尽量用通俗易懂的语言来描述他们之间的关系。
这里我们先来看一下Java方法和C/C++函数的对应关系,消息机制中所有的JNI调用都可以从这里找到对应,这部分内容都在android_os_MessageQueue.cpp文件中
frameworks/base/core/jni/android_os_MessageQueue.cpp
class NativeMessageQueue : public MessageQueue, public LooperCallback {
public:
//函数声明 构造函数
NativeMessageQueue();
virtual ~NativeMessageQueue();
virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);
//函数声明 在Java层对应nativePollOnce
void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis);
//函数声明 在Java层对应nativeWake
void wake();
void setFileDescriptorEvents(int fd, int events);
virtual int handleEvent(int fd, int events, void* data);
//3个私有的成员变量
private:
JNIEnv* mPollEnv;
jobject mPollObj;
jthrowable mExceptionObj;
};
//JNI调用对应函数关系
static const JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{
"nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
{
"nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
{
"nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
{
"nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
{
"nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling },
{
"nativeSetFileDescriptorEvents", "(JII)V",
(void*)android_os_MessageQueue_nativeSetFileDescriptorEvents },
};
nativeInit()函数最终调用的android_os_MessageQueue_nativeInit() 代码。
{
"nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit }
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
//调用NativeMessageQueue的构造函数
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
nativeMessageQueue->incStrong(env);
//强制类型转换reinterpret_cast 返回一个jlong类型 就是java的long类型
return reinterpret_cast<jlong>(nativeMessageQueue);
}
这个函数的实现通俗点的理解就是以下两点:
-
通过调用
NativeMessageQueue构造函数创建一个NativeMessageQueue*的指针变量nativeMessageQueue,可以理解成Java对象引用。 -
将
nativeMessageQueue指针变量强制类型转换成Java的long类型,通过函数返回值赋值给Java层的MessageQueue中成员变量mPtr,可以理解成mPtr保存了nativeMessageQueue这个指针变量的的内存地址
这样MessageQueue和NativeMessageQueue就建立了一定的关联。
mPtr = nativeInit();
long mPtr = nativeMessageQueued 的内存地址 0x00009527; //这个我瞎写的地址啊!
这里我们看下NativeMessageQueue构造函数,发现这里又调用了Looper的构造函数,这里的Looper可不是Java层的Looper而是位于C/C++层的Looper
//NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
//内部由调用了Looper的构造函数 此Looper不是Java层的Looper而是C/C++层的Looper
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
总结:
MessageQueue通过JNI调用构建了NativeMessageQueue对象,并建立关联;NativeMessageQueue构造函数中调用Looper对象的构造函数构建了Looper对象。

和Java层的创建关系正好相反,而最终的构建链路:

这里C/C++层的Looper构造函数。我们并没有深入分析,C/C++层的Looper在整个消息机制中起到了很重要的作用就是阻塞和唤醒,我们会接下来先从Java层Looper开启消息循环这个入口来慢慢深入。
前置知识2
在看Looper构造之前,我希望大家对于Linux的pipe/eventfd 以及epoll机制有一定的理解,如果没有,我推荐先看下面的两篇文章进行入门,先弄清楚这些是什么,用来干什么,至于实现原理可以先不深究。
Android消息处理机制为什么要了解【进程间通信】?
Android消息处理机制我们可以看成是【线程间通信】,那么了解【进程间通信】有助于我们理解【线程间通信】因为线程我们也称之为“轻量级进程”,而且在Linux内核中,线程和进程采用同样的

本文详细介绍了Android消息处理机制,重点关注消息循环中的阻塞和唤醒。文章首先强调了理解进程间通信和IO多路复用的重要性,然后阐述了MessageQueue、Looper和Handler的创建过程,特别是Looper在消息循环中的角色。通过分析Looper的构造函数,揭示了如何利用epoll机制进行阻塞和唤醒。文章还讨论了不同Android版本中Looper的实现差异,并解释了消息发送和唤醒的过程,以及消息如何被处理。最后,作者强调了基础知识在深入理解这一机制中的关键作用。
最低0.47元/天 解锁文章
277

被折叠的 条评论
为什么被折叠?



