Android 之 MessageQueue

本文深入解析Android中的MessageQueue,探讨其构造与初始化过程,详细解释消息的发送与接收机制,以及如何通过Linux管道机制实现线程间的高效通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MessageQueue 是一个消息队列,可放可取

1.先看其构造函数,不是共有的,开发者不能创建这个对象,必须有系统去创建和初始化;在构造函数中调用了一个nativeInit方法;

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    nativeInit();
}


2.nativeInit方法的实现什么东西?对应底层的C++函数如下,在android_os_MessageQueue.cpp中:

static voidandroid_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
    // NativeMessageQueue是MessageQueue在Native层的代表
    NativeMessageQueue* nativeMessageQueue = newNativeMessageQueue();
    ......
    // 将这个NativeMessageQueue对象设置到Java层保存
    android_os_MessageQueue_setNativeMessageQueue(env,obj,nativeMessageQueue);
}
从上面代码可以看出,这个MessageQueue队列是在底层创建的;



3.我们在看看NativeMessageQueue对象的构造函数:

NativeMessageQueue::NativeMessageQueue() {
    /* 代表消息循环的Looper也在Native层中呈现身影了。根据消息驱动的知识,一个线程会有一个
      Looper来循环处理消息队列中的消息。下面一行的调用就是取得保存在线程本地存储空间
     (Thread Local Storage)中的Looper对象 */
    mLooper= Looper::getForThread();
    if (mLooper == NULL) {
        /* 如为第一次进来,则该线程没有设置本地存储,所以须先创建一个Looper,然后再将其保存到
          TLS中,这是很常见的一种以线程为单位的单例模式*/
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}
这里出现了一个Looper,但是这个和java层的Looper是两个不同的概念;是native层参与消息轮询的参与者;


4.那么是如何发送消息的呢?在MessageQueue中有的enqueueMessage方法;

final boolean enqueueMessage(Message msg, longwhen) {
    ......
    finalboolean needWake;
    synchronized (this) {
        if(mQuiting) {
           return false;
        }else if (msg.target == null) {
           mQuiting = true;
        }
       msg.when = when;
       Message p = mMessages;
        if(p == null || when == 0 || when < p.when) {
           /* 如果p为空,表明消息队列中没有消息,那么msg将是第一个消息,needWake
             需要根据mBlocked的情况考虑是否触发 */
           msg.next= p;
           mMessages = msg;
           needWake = mBlocked;
        } else {
           // 如果p不为空,表明消息队列中还有剩余消息,需要将新的msg加到消息尾
           Message prev = null;
           while (p != null && p.when <= when) {
               prev = p;
               p = p.next;
           }
           msg.next = prev.next;
           prev.next = msg;
           // 因为消息队列之前还剩余有消息,所以这里不用调用nativeWakeup
           needWake = false;
        }
    }
    if(needWake) {
        // 调用nativeWake,以触发nativePollOnce函数结束等待
       nativeWake(mPtr);
    }
    returntrue;
}
将消息按照时间的顺序插入到消息队列中,然后去唤醒主线程调用nativeWake唤醒;代码如下:

static voidandroid_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr){
   NativeMessageQueue* nativeMessageQueue = // 取出NativeMessageQueue对象
                      reinterpret_cast<NativeMessageQueue*>(ptr);
    returnnativeMessageQueue->wake(); // 调用它的wake函数
}
void NativeMessageQueue::wake() {
   mLooper->wake(); // 层层调用,现在转到mLooper的wake函数
}
//Native Looper的wake函数代码如下:
void Looper::wake() {
    ssize_tnWrite;
    do {
        // 向管道的写端写入一个字符
       nWrite = write(mWakeWritePipeFd, "W", 1);
    } while(nWrite == -1 && errno == EINTR);
}
可以看到唤醒的方法是在管道中写了一个字符“w”;这样MessageQueue中的next方法就可以调用底层的nativePollOnce就可以读到唤醒消息,唤醒主线程;

5.当java层在ActivityThread中的main方法中调用了Looper.loop()后,主线程就进入一个while(true)循环中,循环调用Messagequeue的next()方法;下面就看看next中做了什么?是如何阻塞主线程的?

final Message next() {
    int pendingIdleHandlerCount = -1;
    int nextPollTimeoutMillis = 0;
    for (;;) {
        ......
        // mPtr保存了NativeMessageQueue的指针,调用nativePollOnce进行等待
        nativePollOnce(mPtr,nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            // mMessages用来存储消息,这里从其中取一个消息进行处理
            final Message msg = mMessages;
            if (msg != null) {
                final long when = msg.when;
                if (now >= when) {
                    mBlocked = false;
                    mMessages = msg.next;
                    msg.next = null;
                    msg.markInUse();
                    return msg; // 返回一个Message给Looper进行派发和处理
               } else {
                    nextPollTimeoutMillis = (int) Math.min(when- now,Integer.MAX_VALUE);
                }
            } else {
                nextPollTimeoutMillis = -1;
            }
            ......
            /* 处理注册的IdleHandler,当MessageQueue中没有Message时,
           Looper会调用IdleHandler做一些工作,例如做垃圾回收等  */
           ......
           pendingIdleHandlerCount = 0;
           nextPollTimeoutMillis = 0;
        }
    }
}
在上面的代码中,又是一个死循环,定义了一个long类型的mPtr变量传入底层;

在看看nativiePollOnce这个函数,函数会阻塞,读取管道中的唤醒主线程的信息,读不到会阻塞;

static voidandroid_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jintptr, jint timeoutMillis)
    NativeMessageQueue* nativeMessageQueue =
                           reinterpret_cast<NativeMessageQueue*>(ptr);
    // 取出NativeMessageQueue对象,并调用它的pollOnce
   nativeMessageQueue->pollOnce(timeoutMillis);
}
void NativeMessageQueue::pollOnce(inttimeoutMillis) {
   mLooper->pollOnce(timeoutMillis); // 重任传递到Looper的pollOnce函数
}
inline int pollOnce(int timeoutMillis) {
    return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
发现调用进去后,是层层调用,最后调用了native层的轮询器mLooper的pollOnce函数;最终是在这个函数中实现阻塞式读取管道的唤醒信息;

MessageQueue中实现了消息的发送,轮询接受,移除,还是实现了利用liunx的通道机制实现线程的唤醒,这部分代码是在底层实现的;





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值