Android消息系统原理


消息机制

Android异步,线程间通信是消息机制,App架构都是基于消息,ActivityThread类main方法,主线程启动一个Looper和消息队列,组件启动,视图绘制通过主线程从消息队列获取到消息通知实现。
生产者-消费者线程模型,消费者从队列获取,消息处理,同时实现休眠+唤醒,生产者将消息插入队列,实现生产者线程对消费者线程的通知。

Android消息系统原理_第1张图片
生产者-消费者模型

一个线程,一旦启动Looper,run方法将不会结束,在ActivityThread的main方法,最后一句Looper.loop(),启动主线程Looper。App主线程会一直运行,(这里的运行指执行和休眠),否则,主线程挂掉将抛出异常。

Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");

消息机制关键技术是消费者都休眠和唤醒,当消息队列是空时,消费者休眠,防止空跑,占用CPU资源,当消息队列不是空时,消费者唤醒。根据这种设计需求,两个线程+一个阻塞队列可以实现,即利用Java中Condition的await和signal机制。
Android采用Native层epoll方案实现。Android消息系统一共分两层,Java层和Native层,Java层的类,Handler类,负责消息创建、发送、回调,Message类,具体消息体对象。MessageQueue类,消息队列,负责维护和控制消息吞吐。Looper类,负责执行线程循环。
Native层的类,NativeMessageQueue类,Looper类,负责线程休眠唤醒。


消息队列和Looper

Android消息系统原理_第2张图片
消息队列和Looper结构

启动Looper时,首先调用Looper的prepare方法,创建Looper对象。

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

ThreadLocal类确保每个线程有一个Looper实例,线程间Looper对象不会相互影响。主线程Looper创建是prepareMainLooper方法,在Looper类的sMainLooper静态对象,未使用ThreadLocal类存储。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

创建消息队列,主线程消息队列不允许quit退出。

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

JNI方法,初始化Native层NativeMessageQueue对象,返回mPtr指针,创建Native层Looper。

NativeMessageQueue::NativeMessageQueue() :
                mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);//创建底层Looper实例。
        Looper::setForThread(mLooper);
    }
}

Native层的Looper对象线程唯一性,它是消息系统的核心,采用epoll机制,实现线程休眠和唤醒。

Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd = eventfd(0, EFD_NONBLOCK);//epoll监听这个句柄。
    AutoMutex _l(mLock);
    rebuildEpollLocked();
}

初始化一个文件描述符,rebuildEpollLocked方法,创建epoll句柄,注册事件。

void Looper::rebuildEpollLocked() {
    ...
    //创建epoll句柄
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); 
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    //监听mWakeEventFd
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
    
    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);
    }
}

epoll_create方法,创建mEpollFd句柄,epoll_ctl方法,向句柄注册内核监听的描述符,包括流,管道,文件描述符等。
Native层Looper内部包含一个epoll_ctl方法注册的mWakeEvenFd描述符,epoll机制实现在Linux内核监听多个描述符事件。


Handler类

Android消息系统原理_第3张图片
Handler类工作原理

在Java层,Handler类是消息创建和回调工具类。

public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler绑定消息队列和Looper,构造参数可以指定Looper对象。

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

Handler发送消息,向消息队列插入消息。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

将消息target设置成Handler本身,调用消息队列enqueueMessage方法。任何线程都可以通过它发送消息到内部消息队列,队列由消费者线程访问处理,实现任何线程向消费者线程的消息通知。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

消费者线程Looper.loop()等待,从消息队列获取消息,从消息获取target(发送者Handler)回调。消息处理优先级,消息体Callback,Handler内部Callback,重写handleMessage方法。


工作原理

启动Looper,消费者线程将在Looper.loop()循环,通过消息队列next方法,等待消息。

public static void loop() {
    //当前线程Looper。
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    //无限循环
    for (;;) {
        Message msg = queue.next(); // 这里有可能休眠
        if (msg == null) {
            // 没有消息,代表消息队列已经退出,将结束循环。
            return;
        }
        //Message消息处理
        try {
            msg.target.dispatchMessage(msg);
        } finally {
        }
        ...
    }
}

当消息队列是空时,MQ.next()方法将进入休眠,

Message next() {
    final long ptr = mPtr;
    //底层消息队列已经销毁,直接退出循环。
    //Looper.loop收到null时,也将退出for循环,结束线程。
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;//第一次进入时,不设置休眠等待时间。
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //JNI方法,底层休眠,第一次循环休眠时间是0。
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // 如果队列第一个是同步栅栏消息,则跳过后续的同步消息,直接找到异步消息执行,确保异步消息的优先级高。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            //若不是同步栅栏,msg就是队列的第一个消息。
            if (msg != null) {
                if (now < msg.when) {
                    // 发现此时还未到消息的执行时间,设置差值,将继续循环,休眠差值时间。
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 此刻已经到达消息设定执行时间,消息返回给loop方法。
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 若消息队列已经空了,设置无限等待休眠,直到手动唤醒,在插入时。
                nextPollTimeoutMillis = -1;
            }
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
            ...
        }
        ...
    }
}

JNI#方法,nativePollOnce方法,第一个参数Native层消息队列指针,第二个参数nextPollTimeoutMillis休眠时间,根据当前时间和队列中消息delay延迟执行的目标时间,计算nextPollTimeoutMillis,通知Native层Looper,最终调用Native层Looper的pollInner方法。

int Looper::pollInner(int timeoutMillis) {
    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    //休眠
    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    //遍历事件数量
    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) {
                awoken();
            } 
        } 
    }
Done: ;
    // 处理mMessageEnvelopes中的message,拿到handler。
    // 这里面的消息是在底层发送的,底层Looper#sendMessage方法。
    mNextMessageUptime = LLONG_MAX;
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            { // obtain handler
                sp handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();
                //执行MessageHandler的handleMessage消息。
                handler->handleMessage(message);
            } // release handler

            mLock.lock();
            mSendingMessage = false;
            result = POLL_CALLBACK;
        } else {
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }
    mLock.unlock();
    return result;
}

epoll_wait方法,线程休眠。第一个参数mEpollFd,epoll_create方法创建的epoll句柄,第二个参数eventItems,监听的事件集合,第四个参数,休眠时间,返回值eventCount,监听到的事件数量。

举例,Java层消息队列第一个消息的执行时间when超过当前时间1分钟,表示消息延迟执行,间隔1分钟,通过JNI的nativePollOnce方法,通知Native层Looper在epoll_wait方法处休眠1分钟。

自动唤醒,一旦设定休眠时间,到达时将自动唤醒,继续执行,遍历事件数量,回到上层消息队列获取消息。因休眠时间是经上层计算的,这次回去会达到第一个消息的执行时间。
非自动唤醒,如果是mWakeEventFd描述符事件,代表监听到事件,不是自动唤醒,是Java层插入消息的唤醒,调用awoken方法。
当上层消息队列是空时,设置timeoutMillis值是-1,epoll_wait方法的唤醒时间不确定。
最后,在Done代码块,处理Native层Looper的sendMessage方法发送的消息。

总结:
Looper.loop()方法,不会一直占用CPU资源,当消息队列是空时,不设定休眠唤醒时间,epoll_wait方法一直休眠。
消息队列根据消息执行时间when控制Native层Looper休眠唤醒间隔,一旦Looper启动,MQ.next()方法将一直做这件事情。
在Native层,Looper的pollInner方法并非只通过上层调用,其他需要利用Looper唤醒休眠机制的Native层位置也触发此方法,回调将由Native层MessageHandler负责。

Handler类调用MQ.enqueueMessage()方法,向队列插入消息,是另一种唤醒方式。

boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //消息队列是空,或立即执行,或按时间排序插入头部。设置唤醒标志。
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            //若第一个消息是同步栅栏(即没有派发目标),且插入消息是异步消息。
            //不管怎样,都需要唤醒
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                //队里已经有异步消息啦,不需要唤醒。
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

主动唤醒标志,当队列是空或者when是0,代表立即执行,或者执行时间when小于头部消息时间,插入位置在头部,按照时间先后排序,设置主动唤醒标志。
当不满足上述条件时,如果存在同步栅栏,个人理解是遇到SyncBarrier这个消息,优先处理后续的异步消息,屏蔽同步消息,SyncBarrier消息没有target目标,在Android SDK中是hide状态,不会暴漏给App调用,因此,在App中不会发送这类消息。总之,从源码中可知,当队列头部遇到SyncBarrier消息(没有target),且插入消息是异步,将会主动唤醒,除非队列中已经有其他异步消息了。
最后,根据唤醒标志,调用JNI方法,nativeWake方法,立即唤醒。当不需要唤醒,将消息按时间排序插入到队列合适位置,不会执行唤醒方法,消费者线程继续休眠,唤醒由MQ.next()方法决定。

void Looper::wake() {
    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

wake方法,向mWakeEventFd句柄写入一些内容,(插入可能发生在其他任何线程中噢!),这时,Looper.loop()消费者线程正在MQ.next()处,epoll_wait休眠,监听到mWakeEventFd事件。当注册的流发生了事件,(这里写入的内容不是重点)。唤醒,检查是mWakeEventFd事件,awoken方法,读取流的内容,(这里读取的内容也不是重点),关键是线程被唤醒,可以继续执行,从JNI层回到Java层MQ.next(),获取消息并回调。
Native层消息,存储在Vector类型的mMessageEnvelopes对象,唤醒后直接处理,遍历MessageEnvelopes数组内容。上层消息,返回到Java层MQ.next()和Looper.loop(),进行回调处理。

Android消息系统原理_第4张图片
工作原理

向队列插入消息,唤醒时机,在消息发送到MQ之后,由MQ. enqueueMessage()判断,当不唤醒时,说明消息不需要立即执行。


总结与扩展

Native层Looper,利用epoll提供消费者线程休眠和唤醒的机制。
自动唤醒,根据上层消息队列对消息执行时间when的判断,决策唤醒timeout。
主动唤醒,Handler类主导,消息队列决策,向消费线程epoll监听的文件描述符写入字段,触发流事件,即可唤醒。
按照消息执行时间升序插入队列位置。
Java层功能,消息创建发送,队列插入维护,唤醒时机间隔计算决策,消息处理。
Native层消息,这种场景不经过上层。
扩展,epoll机制有两种模式,ET边缘模式与LT水平模式,(默认模式)。
ET模式,状态发生变化时才会事件通知,例如,向流中写入10个字节,消费者线程唤醒,仅读取5个字节,再次循环到此处时,将不会再收到通知,除非再次写入句柄改变状态。
LT模式,有数据留在buffer未读取,每次循环到此处时,会一直收到事件通知。


任重而道远

你可能感兴趣的:(Android消息系统原理)