2.android消息机制-native

1 几个简单类

  • MessageQueue — 里面有一个Looper,和java层的MessageQueue同名;
  • NativeMessageQueue — MessageQueue的继承类,native层的消息队列,只是一个代理类,其大部分方法操作都转交给Looper的方法;
  • Looper — native层的Looper,其功能相当于java层的Handler,它可以取出消息,发送消息,处理消息;
  • MessageHandler — native层的消息处理类,Looper把处理消息逻辑转交给此类;
  • WeakMessageHanlder — MessageHandler的继承类,也是消息处理类,但最终还是会把消息处理逻辑转交给MessageHandler。

2 MessageQueue

  • nativeInit Java层Looper构造->MessageQueue构造->nativeInit
  • nativePollOnce Java层取消息 next方法时调用
  • nativeWake java层的MessageQueue的enqueueuMessage方法调用
//MessageQueue.java
public final class MessageQueue {
    private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    private native void nativePollOnce(long ptr, int timeoutMillis); 
    private native static void nativeWake(long ptr);
    private native static boolean nativeIsPolling(long ptr);
    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
    //...
}

2.1 nativeInit()

java层,在ActivityThread的main方法创建UI线程的消息循环,Looper.prepareMainLooper -> Looper.prepare -> new Looper -> new MessageQueue

//MessageQueue.java
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {   
    //创建native消息队列NativeMessageQueue
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    //...
    //增加引用计数
    nativeMessageQueue->incStrong(env);
    //使用C++强制类型转换符reinterpret_cast把NativeMessageQueue指针强转成long类型并返回到java层
    return reinterpret_cast(nativeMessageQueue);
}

//frameworks/base/core/jni/android_os_MessageQueue.cpp
NativeMessageQueue::NativeMessageQueue() 
    : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    //获取TLS中的Looper(Looper::getForThread相当于java层的Looper.mLooper中的ThreadLocal.get方法) 
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        //创建native层的Looper
        mLooper = new Looper(false);
        //保存Looper到TLS中(Looper::setForThread相当于java层的ThreadLocal.set方法)
        Looper::setForThread(mLooper);
    }
}

//system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    //1、构造唤醒事件的fd(文件描述符)
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    //...
    //2、重建epoll事件
    rebuildEpollLocked();
}

文件描述符是什么?它就是一个int值,又叫做句柄,在Linux中,打开或新建一个文件,它会返回一个文件描述符,读写文件需要使用文件描述符来指定待读写的文件,所以文件描述符就是指代被打开的文件,所有对这个文件的IO操作都要通过文件描述符

但其实文件描述符也不仅仅是指代文件,它还有更多的含义,可以看后文的epoll机制解释。

//system/core/libutils/Looper.cpp
void Looper::rebuildEpollLocked() {
    //1、关闭旧的管道
    if (mEpollFd >= 0) {
        close(mEpollFd);
    }

    //2、创建一个新的epoll文件描述符,并注册wake管道
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);//EPOLL_SIZE_HINT为8

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); //置空eventItem
    //3、设置监听事件类型和需要监听的文件描述符
    eventItem.events = EPOLLIN;//监听可读事件(EPOLLIN)
    eventItem.data.fd = mWakeEventFd;//设置唤醒事件的fd(mWakeEventFd)

    //4、将唤醒事件fd(mWakeEventFd)添加到epoll文件描述符(mEpollFd),并监听唤醒事件fd(mWakeEventFd)
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);   
    
    //5、将各种事件,如键盘、鼠标等事件的fd添加到epoll文件描述符(mEpollFd),进行监听
    for (size_t i = 0; i < mRequests.size(); i++) {
        const Request& request = mRequests.valueAt(i);
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
        if (epollResult < 0) {
            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
                  request.fd, strerror(errno));
        }
    }
}
}

Looper构造函数涉及Linux epoll机制,epoll机制是Linux最高效的I/O复用机制

2.2 nativePollOnce()

在native层通过epoll机制也建立了一套消息机制后,java层的消息循环也就创建好,在此之后就会在java层中启动消息循环,Looper.loop -> MessageQueue.next,在java层中每次循环去读消息时,都会调用MessageQueue的next函数,如下:

//MessageQueue.java
 Message next() {
 	//...
 	for(;;){
 		 nativePollOnce(ptr, nextPollTimeoutMillis);
 		 //...
 	}
 	//...
 }

//frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    //把ptr强转为NativeMessageQueue
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

//frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    //...
    //核心是调用了native层的Looper的pollOnce方法
    mLooper->pollOnce(timeoutMillis);  
    //...
}
//NativeMessageQueue是一个代理类,所以它把逻辑转交给Looper,
//system/core/libutils/Looper.h
inline int pollOnce(int timeoutMillis) {
    //调用了带4个参数的pollOnce方法
    return pollOnce(timeoutMillis, NULL, NULL, NULL);
}

//system/core/libutils/Looper.cpp
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    //一个死循环
    for (;;) {
        //...
        //当result不等于0时,就会跳出循环,返回到java层
        if (result != 0) {
            //...
            return result;
        }
        //处理内部轮询
        result = pollInner(timeoutMillis);
    }
}

pollInner方法返回一个int值result,代表着本次轮询是否成功处理了消息,当result不等于0时,就会跳出循环,返回到java层继续处理java层消息,result有以下4种取值:

enum {
    //表示Looper的wake方法被调用,即管道的写端的write事件触发
    POLL_WAKE = -1,
    //表示某个被监听fd被触发。
    POLL_CALLBACK = -2,
    //表示等待超时
    POLL_TIMEOUT = -3,
    //表示等待期间发生错误
    POLL_ERROR = -4,
};
//system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) { 
    //timeoutMillis等于-1,并且mNextMessageUptime不等于LLONG_MAX
    //这说明java层没有消息但是native层有消息处理,这时在epoll_wait中,线程不能因为timeoutMillis等于-1而进入休眠,它还需要处理native层消息
    //所以这里会根据mNextMessageUptime把timeoutMillis更新为大于0的值
    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
        if (messageTimeoutMillis >= 0
                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
            //更新timeoutMillis为大于0的值,这个大于0的值就是需要等待多久后,才会到达native层消息的执行时间,等待timeoutMillis后,epoll_wait就会返回处理native层消息
            timeoutMillis = messageTimeoutMillis;
        }
        //...
    }
    int result = POLL_WAKE;
    //...
    //事件集合(eventItems),EPOLL_MAX_EVENTS为最大事件数量,它的值为16
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    
    //1、等待事件发生或者超时(timeoutMillis),如果有事件发生,就从管道中读取事件放入事件集合(eventItems)返回,如果没有事件发生,进入休眠等待,如果timeoutMillis时间后还没有被唤醒,就会返回
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);  
    
    //获取锁
    mLock.lock();
    
    //...省略的逻辑是:如果eventCount <= 0 都会直接跳转到Done:;标记的代码段

    //2、遍历事件集合(eventItems),检测哪一个文件描述符发生了IO事件
    for (int i = 0; i < eventCount; i++) {
        //取出文件描述符
        int fd = eventItems[i].data.fd;
        //取出事件类型
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd) {//如果文件描述符为mWakeEventFd
            if (epollEvents & EPOLLIN) {//并且事件类型为EPOLLIN(可读事件)
                //这说明当前线程关联的管道的另外一端写入了新数据
                //调用awoken方法不断的读取管道数据,直到清空管道
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {//如果是其他文件描述符,就进行它们自己的处理逻辑
           //...
        }
    }
    
    //2、下面是处理Native的Message
    Done:;
    //mNextMessageUptime如果没有值,会被赋值成LLONG_MAX,但是如果mNextMessageUptime已经有值,它还是保持原来的值
    mNextMessageUptime = LLONG_MAX;
    //mMessageEnvelopes是一个Vector集合,它代表着native中的消息队列
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        //取出MessageEnvelope,MessageEnvelop有收件人Hanlder和消息内容Message
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        //判断消息的执行时间
        if (messageEnvelope.uptime <= now) {//消息到达执行时间
            {
        	    //获取native层的Handler
                sp<MessageHandler> handler = messageEnvelope.handler;
                //获取native层的消息
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                //释放锁
                mLock.unlock();
                //通过MessageHandler的handleMessage方法处理native层的消息
                handler->handleMessage(message);
            }
            mLock.lock();
            mSendingMessage = false;
            //result等于POLL_CALLBACK,表示某个监听事件被触发
            result = POLL_CALLBACK;
        } else {//消息还没到执行时间
            //把消息的执行时间赋值给mNextMessageUptime
            mNextMessageUptime = messageEnvelope.uptime;
            //跳出循环,进入下一次轮询
            break;
        }
    }
    //释放锁
    mLock.unlock();
    //...
    return result;
}

2.3 处理native message

只要epoll_wait方法返回后,都会进入Done标记位的代码段, 就开始处理处理native层的Message, 在此之前先讲解一下MessageEnvelope,正如其名字,信封,其结构体定义在Looper.h中,如下:

//system/core/libutils/Looper.h
class Looper : public RefBase { 
    struct MessageEnvelope {
        MessageEnvelope() : uptime(0) { }

        MessageEnvelope(nsecs_t u, const sp<MessageHandler> h, const Message& m) : uptime(u), handler(h), message(m) {}

        nsecs_t uptime;
        //收信人handler
        sp<MessageHandler> handler;
        //信息内容message
        Message message;
    };   
    //...
}

MessageEnvelope里面记录着收信人(handler,MessageHandler类型,是一个消息处理类),发信时间(uptime),信件内容(message,Message类型),Message结构体,消息处理类MessageHandler都定义在Looper.h文件中, 在java层中,消息队列是一个链表,在native层中,消息队列是一个C++的Vector向量,Vector存放的是MessageEnvelope元素,接下来就进入一个while循环,里面会判断消息是否达到执行时间,如果到达执行时间,就会取出信封中的MessageHandler和Message,把Message交给MessageHandler的handlerMessage方法处理;如果没有到达执行时间,就会更新mNextMessageUptime为消息的执行时间,这样在下一次轮询时,如果由于java层没有消息导致timeoutMillis等于-1,就会根据mNextMessageUptime更新timeoutMillis为需要等待执行的时间,超时后返回继续处理native层消息队列的头部信息。

3 nativeWake()

Java层通过Hanlder发送消息时,实际是把消息添加到消息队列,Handler.sendXX -> Handler.enqueueMessage -> MessageQueuue.enqueueMessage,最终会调用到MessageQueue的enqueueMessage方法,,如下:

//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    //...
    synchronized (this) {
        //...
        if (needWake) {
            nativeWake(mPtr);
        }
    }
}
//frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}
//NativeMessageQueue只是一个代理Looper的角色,该方法把操作转发给native层的Looper
//frameworks/base/core/jni/android_os_MessageQueue.cpp
void NativeMessageQueue::wake() {
    mLooper->wake();
}

//system/core/libutils/Looper.cpp
void Looper::wake() {
    uint64_t inc = 1;
    //使用write函数通过mWakeEventFd往管道写入字符inc
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    //...
}

Looper的wake方法其实是使用write函数通过mWakeEventFd往管道写入字符inc,其中TEMP_FAILURE_RETRY 是一个宏定义, 当执行write方法失败后,会不断重复执行,直到执行成功为止,在nativeinit中,我们已经通过epoll_create方法监听了mWakeEventFd的可读事件,当mWakeEventFd可读时,epoll文件描述符就会监听到,这时epoll_wait方法就会从管道中读取事件返回,返回后就执行消息处理逻辑,所以这里的往管道写入字符inc,其实起到一个通知的作用,告诉监听的线程有消息插入了消息队列了,快点醒过来(因为进入了休眠状态)处理一下。

为什么方法阻塞却又不消耗性能

epoll_wait方法会让当前线程释放CPU资源进入休眠状态,等到下一个消息到达(mWakeEventFd会往管道写入字符)或监听的其他事件发生时就会唤醒线程,然后处理消息,

你可能感兴趣的:(消息机制,android)