Handler机制(Native层)

221 阅读6分钟

handler主要处理android中的消息机制。它的实现不仅仅在java层,与native也是息息相关的。熟悉native层需要先理解linux的I/O多路转接服务epooll机制

1. epooll机制接口

epoll_create

epoll_create()函数用于创建一个epoll句柄,并请求内核为该实例后期需存储的文件描述符及对应事件预先分配存储空间,并不是表示最大可使用的文件标识符的数量

epoll_create(int size
epoll_create1(flag)

epoll_create1 产生一个epoll 实例,返回的是实例的句柄。flag 可以设置为0 或者EPOLL_CLOEXEC,为0时函数表现与epoll_create一致,EPOLL_CLOEXEC标志与open 时的O_CLOEXEC 标志类似,即进程被替换时会关闭文件描述符

epoll_ctl()

epoll_ctl()是epoll的事件注册函数,用于将文件描述符添加到epoll的文件描述符集中,或从集合中删除指定文件描述符

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll_ctl()函数中的参数epfd为函数epoll_create()返回的epoll句柄;参数op表示epoll_ctl()的动作,该动作的取值由三个宏指定,这些宏及其含义分别如下:

● EPOLL_CTL_ADD表示epoll_ctl()将会在epfd中为新fd注册事件; ● EPOLL_CTL_MOD表示epoll_ctl()将会修改已注册的fd监听事件; ● EPOLL_CTL_DEL表示epoll_ctl()将会删除epfd中监听的fd

epoll_wait()

epoll_wait()函数用于等待epoll句柄epfd中所监控事件的发生,当有一个或多个事件发生或等待超时后epoll_wait()返回

int epoll_wait(int epfd, struct epoll_event *events,
​              int maxevents, int timeout);
  1. 参数epfd为epoll_create()函数返回的句柄;
  2. 参数events指向发生epoll_create()调用时系统事先预备的空间,当有监听的事件发生时,内核会将该事件复制到此段空间中;
  3. 参数maxevents表示events的大小,该值不能超过调用epoll_create()时所传参数size的大小;
  4. 参数timeout的单位为毫秒,用于设置epoll_wait()的工作方式:若设置为0则立即返回,设置为-1则使epoll无限期等待,设置为大于0的值表示epoll等待一定的时长。

使用实例

//1. 创建epoll句柄
efd = epoll_create(OPEN_MAX);
//为服务器进程注册事件(listenfd)
//eventfd

struct epoll_event {
  __uint32_t  events;   //epoll事件
  epoll_data_t data;    //用户数据变量
};

//2. 注册新的事件
struct epoll_event tep;
//2.1 一个socket 监听事件
istenfd = Socket(AF_INET, SOCK_STREAM, 0);
tep.events = EPOLLIN;
tep.data.fd = listenfd;
epoll_ctl(efd, EPOLL_CTL_ADD, istenfd, &tep);
//2.2 一个accept接收事件
connfd = accept(listenfd, (struct sockaddr *)&cliaddr,&clilen);
tep.events = EPOLLIN;
tep.data.fd = connfd;
epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);

//3. 阻塞监听
nready = epoll_wait(efd, ep, OPEN_MAX, -1);

EPOLLIN:表示有数据可读,所以它发生的时间必然是有新的tcp数据到来。

2. eventfd

创建一个eventfd对象,或者说打开一个eventfd的文件,类似普通文件的open操作。

int eventfd(unsigned int initval, int flags);

该对象是一个内核维护的无符号的64位整型计数器。初始化为initval的值。 flags可以以下三个标志位的OR结果:

  • EFD_CLOEXEC:FD_CLOEXEC,简单说就是fork子进程时不继承,对于多线程的程序设上这个值不会有错的。
  • EFD_NONBLOCK:文件会被设置成O_NONBLOCK,一般要设置。
  • EFD_SEMAPHORE:(2.6.30以后支持)支持semophore语义的read,简单说就值递减1。 这个新建的fd的操作很简单: read(): 读操作就是将counter值置0,如果是semophore就减1。 write(): 设置counter的值。

注意,还支持epoll/poll/select操作,当然,以及每种fd都都必须实现的close。

3. handler(native)

3.1 Looper初始化

handler native初始化.png 初始化过程如上图所示: 1:ActivityThread在main函数里面调用Looper.prepareMainLooper完成java层looper和MessageQueue的初始化 2. 调用JNI 接口nativeinit接口,去初始化native层Looper 3. 在native looper中完成epool句柄的创建和监听事件的注册

Looper.cpp构造

Looper::Looper(bool allowNonCallbacks)
    : mAllowNonCallbacks(allowNonCallbacks),
      mSendingMessage(false),
      mPolling(false),
      mEpollRebuildRequired(false),
      mNextRequestSeq(0),
      mResponseIndex(0),
      mNextMessageUptime(LLONG_MAX) {
    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));

    AutoMutex _l(mLock);
    rebuildEpollLocked();
}

mWakeEventFd:java层消息队列监听事件,初始化为0,表示java层MessageQueue没有消息。>0表示java 层MessageQueue中消息的格式。epoll通过监听这个文件描述符读写事件来阻塞和唤醒java层的looper阻塞。

epoll创建和事件注册

void Looper::rebuildEpollLocked() {
    // Close old epoll instance if we have one.
    //重置epoll句柄
    if (mEpollFd >= 0) {
        mEpollFd.reset();
    }

    // Allocate the new epoll instance and register the wake pipe.
    //创建新的epoll句柄
    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));

    //epoll事件监听结构体
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    eventItem.events = EPOLLIN;//有数据可读时触发,也就是有message消息到来时触发
    eventItem.data.fd = mWakeEventFd.get();//要监听的事件id
    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &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.get(), 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));
        }
    }
}

主要流程如下:

  1. 创建epoll句柄
  2. 为改epoll句柄注册唤醒事件

3.2 Message事件阻塞过程

2. handler消息处理.png

java层是在Looper.loop()方法里面遍历事件消息的。因为是for循环,如果没有消息会一直重复执行,java层Looper是通过natvie的epoll机制来阻塞执行的,避免MessageQueue为空时,一直遍历。

  @UnsupportedAppUsage
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        1. 判断MessageQueue是否已经关闭,已经关闭的情况下,mPtr==0,这个时候直接返回
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //2. 如果没有消息时,通过epol_wait阻塞一段时间,知道有消息时,被唤醒
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //3. 处理同步屏障机制
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //4. 队列中有消息,但还未到执行时间时,下次进入for循环时,会阻塞一段时间,时间长度就是下一个消息的待执行的剩余时间
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                      //5. 有消息将要执行时,则取出消息并返回,退出for循环,下次进入for循环的阻塞事件依然为初始化的0
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    //6. 没有消息时,设置下次for循环阻塞事件为无限阻塞
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                //7. --------处理idleHandler-------------
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

以上函数的主要处理流程如下:

  1. 判断MessageQueue是否已经dispose,已经关闭的情况下,mPtr==0,这个时候直接返回。然后进入for循环,遍历消息队列
  2. 开始遍历消息队列之前,会通过nativePollOnce,调用native层的epool_wait阻塞监听队列中是否有消息队列时间,有事件时返回,或者超过阻塞时间时返回。for循环首次进入时,nextPollTimeoutMillis为0,也就是立即返回。
  3. 如果当前消息时同步屏障消息,则处理同步屏障机制,详细参考处理同步屏障机制
  4. 如果当前消息还未到执行时间时,则更新下次循环的epoll阻塞事件为消息的剩余执行时间。
  5. 如果当前消息即将达到执行时间,则返回消息,退出for循环
  6. 没有消息时,设也就队列头指向null,则设置下次epoll阻塞事件为-1,表示永久阻塞
  7. 如果没有消息,或者当前消息还未到执行时间,则处理IdleHandler数组中的时间。idlerHandler处理完成后,Messagequeue中可能已经传递了新的message,因此设置nextPollTimeoutMillis阻塞事件为0,避免阻塞待处理消息

mPtr

// Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported.

指向NativeMessageQueue的指针,为0时,表示meesage loop已经退出或者 初始化

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

//android_os_MessageQueue.cpp

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }
    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);//mptr
}

释放

private void dispose() {
    if (mPtr != 0) {
        nativeDestroy(mPtr);
        mPtr = 0;
    }
}

nativePollOnce

epoll_waite的调用时序图如上,我们只看下Looper.cpp中的pollonce函数

//Looper.h
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
inline int pollOnce(int timeoutMillis) {
    return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
}
pollOnce
//
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    int result = 0;
    for (;;) {
    //处理navative层的时间请求,跟java层的message无关
        while (mResponseIndex < mResponses.size()) {
            const Response& response = mResponses.itemAt(mResponseIndex++);
            int ident = response.request.ident;
            if (ident >= 0) {
                int fd = response.request.fd;
                int events = response.events;
                void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
                ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
                        "fd=%d, events=0x%x, data=%p",
                        this, ident, fd, events, data);
#endif
                if (outFd != nullptr) *outFd = fd;
                if (outEvents != nullptr) *outEvents = events;
                if (outData != nullptr) *outData = data;
                return ident;
            }
        }
        //只有pollInner的结果返回不会result时,才会退出for循环
        if (result != 0) {
#if DEBUG_POLL_AND_WAKE
            ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
            if (outFd != nullptr) *outFd = 0;
            if (outEvents != nullptr) *outEvents = 0;
            if (outData != nullptr) *outData = nullptr;
            return result;
        }

        result = pollInner(timeoutMillis);
    }
}

上述代码的处理关键是pollInner函数, 且pollInner的返回值不会为0

pollInner
int Looper::pollInner(int timeoutMillis) {
    ..
    // Poll.
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;

    // We are about to idle.
    mPolling = true;

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    //1. 监听epoll句柄中是否有事件发生, mEpollFd就是epollCreate创建的句柄
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // No longer idling.
    mPolling = false;

    // Acquire lock.
    mLock.lock();

    ...
    // poll error.
    // 表示epoll_wait发生错误返回时间
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
        result = POLL_ERROR;
        goto Done;
    }

    //poll timeout.
    //表示阻塞超时
    if (eventCount == 0) {
        result = POLL_TIMEOUT;
        goto Done;
    }

    //2. 有事件发生
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd.get()) {//5. 处理java层的唤醒事件
            if (epollEvents & EPOLLIN) {//判断是否有可读message
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {//其他事件
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
                int events = 0;
                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;

    ...

    // Release lock.
    mLock.unlock();

    ...
    return result;
}

主要流程:

  1. epoll_wait等待epoll句柄epfd中所监控事件的发生,当有一个或多个事件发生或等待超时后epoll_wait()返回
  2. 当有mWakeEventFd的读事件发生时,epoll_wait就会结束阻塞,此时调用awake方法读出mWakeEventFd句柄中的数据,相当于清空了文件
void Looper::awoken() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ awoken", this);
#endif

    uint64_t counter;
    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));
}

mWakeEventFd是在Looper.cpp构造的时候,通过eventfd创建的一个文件句柄

mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));

3.3 message事件唤醒过程

2. handler唤醒过程.png

enqueueMessage

MessageQueue.java

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

往消息队列添加Message时,需要根据mBlocked情况来决定是否需要调用nativeWake.mBlocked我们再上个章节讲过,当处理IdlerHander之后,该值会赋值为TRUE

wake

Looper.cpp

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
                             mWakeEventFd.get(), nWrite, strerror(errno));
        }
    }
}

wake是通过网evenfd文件描述符mWakeEventFd写入数据实现的,写入数据后,epoll_wait就会监听到可读事件,从未接触阻塞等待,这个java层也就知道有消息进入队列,可以读取了。