Android消息处理机制(阻塞和唤醒)

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

前置知识1:

学习任何知识都是要有一定的前置条件的,比如你要先识字才能“看懂”我这篇文章…

在全面了解Android消息处理机制之前,我希望读者对该机制最好要有一定的了解,至少知道主要由哪些类实现了该机制。

如果你还能看得懂c/c++代码,精通Linux内核,对pipe、eventfd和IO多路复用epoll机制都有深入的了解,那么我劝你就别看了…

本文的侧重点主要在于消息机制中的阻塞和唤醒上,对于Java层面的组成关系会简单的带过,这部分的内容已经烂大街了,如果想对于Java层的源码逐行分析请看下面的源码分析(

Android消息机制源码分析

点了之后先不要打我,我其实真的是准备自己写一篇的

主要组成

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

这个函数的实现通俗点的理解就是以下两点:

  1. 通过调用NativeMessageQueue构造函数创建一个NativeMessageQueue* 的指针变量nativeMessageQueue,可以理解成Java对象引用。

  2. nativeMessageQueue指针变量强制类型转换成Javalong类型,通过函数返回值赋值给Java层的MessageQueue中成员变量mPtr,可以理解成mPtr保存了nativeMessageQueue这个指针变量的的内存地址

这样MessageQueueNativeMessageQueue就建立了一定的关联。

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

总结

  1. MessageQueue通过JNI调用构建了NativeMessageQueue对象,并建立关联;
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值