Android消息机制

这样的消息机制已经耳熟能详,本文不拘泥与此,主要探讨消息机制的c++实现

 




void Java.MessageQueue.next() [

for(;;) {

nativePollOnce();

取消息;

}

for循环是用来找到Java层的消息的,在这个for循环里,msg会一直next去找。但是有一个问题,没消息了,next为空了该怎么办?

void Java.Looper.loop() {

for(;;) {

Message msg = queue.next();

if(msg == null) {

return;

}

}

}

毫无疑问,loop方法就退出了,接下来再也不会从queue中自动取消息了。可是实际上Looper是无限执行的。原因在于,如果没有msg了,那么我们就会在nativePollOnce方法上阻塞了,这也是为什么要有这个方法的原因。


nativePollOnce方法阻塞的原理?

c++是怎么获知我们queue中还有没有消息的呢?原因在于for(;;)中

if(msg.next == null) {

nextPollTimeoutMillis = -1;

}

在下一次跑到for(;;)后,这个值就会传到nativePollOnce中了,因此就会阻塞了。


但是一直会阻塞在这里吗,万一有新消息到来我们还是阻塞吗?

事实上新消息到来就会立马得到执行。我们需要看一看enqueueMessage方法。

void Java.enqueueMessage(Message msg) {

for(;;) {

找到msg应该插入的位置,并插入;

if(原来是没消息的,我插入到头部了) {

nativeWake();

}

}

}

显然我们调用了native方法进行了唤醒操作,这样一来nativePollOnce就跑完了,执行到了下面取消息并执行的代码块了


显然没有nativePollOnce方法就根本做不到上面的效果,因为这是我们很好的释放资源的手段。我们通过Java代码是很难释放cpu资源并且重新获取cpu资源的。nativePollOnce方法除了处理资源释放,还用于处理c++层的消息。

但是c++层又是怎么处理资源的释放呢?


c++阻塞

IO流时的cpu的处理规则有3大方法,select、poll、epoll。这里采用的就是epoll方法。epoll会监听某些流,一旦这些流中有几个流就绪,那么就会遍历这几个流。如果没有,cpu资源就会得到释放。

nativePollOnce方法会先执行到MessageQueue.pollOnce,然后会执行到Looper.pollOnce。内部会执行一个epoll_wait方法。上面也说了,如果Java msg为空就会改变某个值,在取值的无限循环的下一次中,调用nativePollOnce并传入-1,所以epoll_wait也会传入-1,如果传入-1,这里就会被阻塞了,所以我们需要重点研究epoll_wait方法是怎么阻塞的?


epoll_wait

if (list_empty(&ep->rdllist)) {

      /* 没有事件,所以需要睡眠。当有事件到来时,睡眠会被ep_poll_callback函数唤醒。*/

      init_waitqueue_entry(&wait, current); //current进程放在wait这个等待队列中。

      wait.flags |= WQ_FLAG_EXCLUSIVE;

      /* 将当前进程加入到eventpoll的等待队列中,等待文件状态就绪或直到超时,或被信号中断。 */

      __add_wait_queue(&ep->wq, &wait);

       for (;;) {

         /* 执行ep_poll_callback()唤醒时应当需要将当前进程唤醒,所以当前进程状态应该为“可唤醒”TASK_INTERRUPTIBLE  */

         set_current_state(TASK_INTERRUPTIBLE);

         /* 如果就绪队列不为空,也就是说已经有文件的状态就绪或者超时,则退出循环。*/

         if (!list_empty(&ep->rdllist) || !jtimeout)

            break;

         /* 如果当前进程接收到信号,则退出循环,返回EINTR错误 */

         if (signal_pending(current)) {

            res = -EINTR;

            break;

         }

          spin_unlock_irqrestore(&ep->lock, flags);

         /* 主动让出处理器,等待ep_poll_callback()将当前进程唤醒或者超时,返回值是剩余的时间。

从这里开始当前进程会进入睡眠状态,直到某些文件的状态就绪或者超时。

当文件状态就绪时,eventpoll的回调函数ep_poll_callback()会唤醒在ep->wq指向的等待队列中的进程。*/

         jtimeout = schedule_timeout(jtimeout);

         spin_lock_irqsave(&ep->lock, flags);

      }

      __remove_wait_queue(&ep->wq, &wait);

       set_current_state(TASK_RUNNING);

   }

可以看到schedule_timeout处,内部调用了内核某些方法,使得进程睡眠了。


唤醒

ep_poll_callback首先会更改一些参数,然后唤醒进程,上面的for循环就会继续执行,此时,逻辑就通过了。epoll_wait这个函数就成功返回了。


native handler总结

在创建Java层的MessageQueue的时候,native的MessageQueue也因此创建了。同时一个epoll_wake_fd就被创建出来了。然后Looper也得到了创建,这个Looper会被缓存到c++层的TLS中去了。

nativePollOnce。在Java层有一个long值,在执行nativePollOnce的时候,long会转化成native的MessageQueue,于此同时还有一个值标识Java层还有没有msg。最终会执行到Looper的pollOnce方法,然后会执行到epoll_wait方法。如果队列中没元素了,就会在schedule_time方法中使得线程睡眠。

在Java的MessageQueue.enqueueMessage方法中,会调用nativeWake,最终也是调用到Looper.wake,管道就会被写入1,代表了w,同时ep_poll_callback方法也会得到执行,进程就被唤醒了,这个时候epoll_wait方法检测到w,就会返回了,这个时候nativePollOnce也就返回了,所以下面就会继续从Java的Message中取next。

而一旦next为空了,某个值就变成了-1,在Message.next的下一次循环中,又会调用到nativePollOnce,然后转化native的MessageQueue,然后Looper.pollOnce,然后epoll_wait,以此类推。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值