message栅栏机制android,Android消息机制原理

  • Post author:
  • Post category:其他


16e22faf224a4c6a2c2d61d6f94628a8.png

一、前言

本文主要阐述Android消息机制的原理而非使用,网上有很多的文章介绍了如何使用,如不熟悉请先搜索了解。

二、Message Handler Looper 原理(java部分):

基本原理:基于epoll和eventfd。

说明:

Message Handler 与 Looper 以及MessageQueue是java层消息机制的重要组成部分。

其中,Message是消息的实体,它可以被循环使用。

Handler是事件的发起人,它可以将消息发到MessageQueue中。

MessageQueue是消息队列,但是核心的处理部分也都在这个类中。

Looper是一个线程相关的对象,同时也是驱动部分,以保证线程可以一直处理消息。

众所周知,一个线程如果本身没有循环的话,那么它执行完run方法后也就结束了。

但是Looper.looper()里面存在for循环,这是线程不会终止的原因,也是消息机制能够生效的前提。

而looper循环中只做两件事,一是消息获取(从MessageQueue),二是消息分发(交给相应的handler处理)。

其中消息获取的方式是MessageQueue.next()方法。

进入到MessageQueue.next()方法后,发现里面也有一个死循环for(;;)

这一方面说明for循环里面的操作可能一次并不能成功,另一方面只有return或break才能退出循环。

可以看到,循环中只有两个return暗示了程序的执行过程,要么return message,要么return null.

return null 也就代表这个线程的消息机制能力结束。

循环中先注意到有一个native调用

nativePollOnce(ptr, nextPollTimeoutMillis);

暂时忽略,然后有一个if语句

它的判断条件是

if (msg != null && msg.target == null)

但是正常情况下msg都是有target的。所以先看下一段

如果消息不为null,那么会判断当前时间now 是不是超过它所期待的执行时间when。

如果超过的话,该消息应该处理,那么直接返回。

如果时间没到,看一看差值,然后设置nextPollTimeoutMillis为这个值。

如果消息为null,设置nextPollTimeoutMillis=-1

剩余部分先忽略。

这样的话,假如此处没有返回消息,将会进入下个循环。

不过此时nextPollTimeoutMillis有所变化,不是刚开始的0.

因为nativePollOnce是一个native调用,所以进到JNI层android_os_MessageQueue.cpp

对应的函数是

android_os_MessageQueue_nativePollOnce

可以看到,传进的第一个参数ptr转化成了NativeMessageQueue的指针(android_os_MessageQueue_nativeInit时生成)。

而它的pollOnce操作又调用了native的Looper的pollOnce。

然后就到了重点,注意Looper.cpp在system/core/libutils中(Android 6.0)。

结果又来了一个for循环…..

进入到pollInner中

与上层有关的就一句

int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

这里利用了epoll机制,其中参数timeoutMillis是上层传过来的nextPollTimeoutMillis.

如果timeoutMillis小于0,这个函数会一直阻塞在这里直到mEpollFd管理的文件描述符的内容有变化。

如果timeoutMillis大于等于0,会在timeoutMillis后返回(如果这段时间没有什么事情发生的话)。

注意mEpollFd是epoll机制本身的描述符,而Looper创建时添加了一个唤醒描述符mWakeEventFd,

它是通过eventfd创建的专门事件通知的描述符。

这样的话只需向mWakeEventFd里面写文件就可以唤醒epoll_wait函数。

底层提供的调用是wake(),上层也可以通过nativeWake调用来调用。

如此一来便实现了阻塞与唤醒,Android的消息循环得以实现。

补充说明:

上述说明有意的忽略掉了不常用的部分,原理就是我们常用的mHandler.sendMessageXX所发送的消息的处理流程。

但是,上层消息机制还提供了一些其他的机制。

1.障碍机制:添加了一个障碍后,在这个障碍之后的(when)同步消息则不会被执行(会在移除障碍后执行),如图一个栅栏一样,只允许异步消息(isAsynchronous)执行。

上面说的if (msg != null && msg.target == null),其中target为null就代表它是一个障碍。当然,你可以随时添加和移除。

2.IdleHandler机制:你可以添加一个IdleHandler,它会在没有消息或当前时间小于下一个消息的when.来执行IdleHandler的任务queueIdle。

目的就是让线程闲置的时候也可以执行一些不重要的任务。

附:

如何保证对象是线程相关的(也就是不同线程使用的是不同的对象)?

使用了ThreadLocal类。

为什么next方法里加了锁synchronized?

当然是因为多线程,Message是通过链表的方式形成的消息队列,而next操作(执行线程)和enqueueMessage操作(可能发生在别的线程)都会

引起Message队列的变动,所以加锁是合理的。

三、MessageEnvelope,Looper(C++部分)

原理:同Java部分

说明:与java部分不同的是,底层部分几乎一切都发生在Looper.cpp中。

其中MessageEnvelope的功能与java层的Message类一样。

注意:应用开发可能无法使用Looper.cpp因为此部分并没有公开,但是NDK提供了另一种方式也可以进行通信,

即下面的ALooper。

四、ALooper (NDK开发)

原理:同Java部分

说明:应用开发者可以使用NDK提供的ALooper类来间接使用Looper,因为ALooper仅仅是对Looper的包装,并公开了部分接口。

其中add_fd接口是留给开发者可以使用的。

可以在frameworks/basse/native/android/looper.cpp找到Alooper的实现.

也可以在frameworks/native/include/android/looper,h或下载的NDK中的platforms/android-24/arch-x86/usr/include/android/looper.h

找到对应的头文件.

add_fd在Looper中的操作也就是

int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);

将该fd纳入到了epoll观测的范围,这样Looper的pollonce也会执行响应的操作。

总体来说,如果你已经理解epoll机制,理解上面的设计都是很容易的。

本文转自:博客园 – lightverse,转载此文目的在于传递更多信息,版权归原作者所有。