android R--Handler源码分析一

178 阅读5分钟

handler的使用都不陌生,这也是面试的重点,在app开发中我们经常使用handler处理异步更新,线程切换。但是我们使用的java层的handler只是一个壳子,总控制和核心逻辑是在native层。 先看下子线程中的handler的使用套路,主线程的looper在activitythread中已经帮我们处理好了。我们就看下子线程的

new Thread(new Runnable() {  
            public void run() {  
                // step1 调用prepare
                Looper.prepare();
                // 构造handler
                Handler handler = new Handler(){  
                    @Override  
                    public void handleMessage(Message msg) {  
                        Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();  
                    }  
                };  
                // step3 发送消息
                handler.sendEmptyMessage(1);  
                // step2 启动循环
                Looper.loop();
            };  
        }).start();

一般就是这个套路。下面我们分别看下各方法实现

(时序图中的androidLooper表示java层的Looper.java; nativeLooper表示system/core/libutils/Looper.cpp文件,用来区分)

step1 Looper.prepare()时序图

image.png

高清图:www.plantuml.com/plantuml/pn…

从上图可以看出,java层prepare执行之后,将native层的整个消息体系都建立了。包含了native层的Looper\epoll监听。

大致关系:

image.png

看下nativeLooper 的rebuildEpollLocked方法

关于epoll介绍网上很多,我们只需要了解下面的api就可以。 Epoll 是一种高效的管理socket的模型,相对于select和poll来说具有更高的效率和易用性。传统的select以及poll的效率会因为 socket数量的线形递增而导致呈二次乃至三次方的下降,而epoll的性能不会随socket数量增加而下降。标准的linux-2.4.20内核不支持epoll,需要打patch。本文主要从linux-2.4.32和linux-2.6.10两个内核版本介绍epoll。
二、 Epoll的使用
epoll用到的所有函数都是在头文件sys/epoll.h中声明的,下面简要说明所用到的数据结构和函数:
所用到的数据结构
typedef union epoll_data {
void ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

struct epoll_event {
__uint32_t events;    / Epoll events /
epoll_data_t data;    / User data variable /
};
结构体epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件,其中epoll_data 联合体用来保存触发事件的某个文件描述符相关的数据,例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。

epoll_event 结构体的events字段是表示感兴趣的事件和被触发的事件可能的取值为: EPOLLIN :表示对应的文件描述符可以读;重要
EPOLLOUT:表示对应的文件描述符可以写;重要
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;重要
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符设定为edge模式;
所用到的函数:
1、epoll_create函数
函数声明:int epoll_create(int size)
该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。
2、epoll_ctl函数
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event event)
该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值
EPOLL_CTL_ADD 注册、
EPOLL_CTL_MOD 修改、
EPOLL_CTL_DEL 删除
fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功返回0,不成功返回-1
3、epoll_wait函数
函数声明:int epoll_wait(int epfd,struct epoll_event   events,int maxevents,int timeout)
该函数用于轮询I/O事件的发生;
参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:-1会导致epoll_wait无限期阻塞,而指定的超时时间等于零导致epoll_wait立即返回,即 使没有可用事件。 返回值:成功时,返回为请求的IO准备就绪的文件描述符的数目;如果在请求的超时毫秒内没有文件描述符准备就绪,则返回零;发生错误时,返回-1并正确设置errno。

void Looper::rebuildEpollLocked() {
    // 重置epoll
    ......

    // Allocate the new epoll instance and register the wake pipe.
    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));

    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
    // EPOLLIN可读类型
    eventItem.events = EPOLLIN;
    // 设置epoll_event中的data的文件描述符是mWakeEventFd
    eventItem.data.fd = mWakeEventFd.get();
    // 注册mWakeEventFd描述符的事件到epoll,事件由eventItem表示,EPOLLIN(可读)
    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);
        ......
    }
}

到这里java层和native层的消息体系就构建成功了。

step2 Looper.loop()启动循环

public static void loop() {
    final Looper me = myLooper();
    
    // 死循环
    for (;;) {
        // 获取消息对象
        Message msg = queue.next(); // might block
        ......
        try {
            msg.target.dispatchMessage(msg);
        } 
        ......
    }
}

MessageQueue#next()

Message next() {
    

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        // nextPollTimeoutMillis不等于0的话 都会去执行一下binder任务
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 调用nativePollOnce
        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;
            // 如果消息链表不是空的并且遇到了消息屏障,找异步消息
            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());
            }
            if (msg != null) {
                if (now < msg.when) {
                // 消息执行的时间还没到
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    // 设置睡眠时间为msg.when - now
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                //消息的执行时间已经到了,立即返回这个消息执行
                    // 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 {
                // 消息链表是空的,设置nextPollTimeoutMillis = -1 表示无限阻塞直到被唤醒
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
            
            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
            // 当没有消息的时候,处理空闲消息IdleHandler
            // 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.
            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不能睡眠了,因为在处理空闲消息的时候可能有新的消息被插入,所以要立即再进入循环查找可用的消息
        nextPollTimeoutMillis = 0;
    }
}

这段逻辑其实很简单,就是找消息链表中的可用消息,如果找到了就返回给looper去执行,找不到的话要分情况:

  1. 消息链表是空的,那么就设置nextPollTimeoutMillis = -1,表示要睡眠直到被唤醒,并处理空闲消息,空闲消息执行后再设置nextPollTimeoutMillis = 0,因为这个过程可能有新的消息被插入了,所以要再进入循环查找可用消息
  2. 链表不为空,那么就判断消息的执行时间和当前系统时间
  • 如果到执行时间的话,就返回这个消息给looper进行后续的执行;
  • 如果没到执行时间那么设置nextPollTimeoutMillis = msg.when - now,睡眠这么久,来 保证消息的正确执行; 整体流程梳理完了,那么要看看nativePollOnce是怎么实现的?

流程图: image.png 高清图:www.plantuml.com/plantuml/pn…

system/core/libutils/Looper.cpp

int Looper::pollInner(int timeoutMillis) {

    // Adjust the timeout based on when the next message is due.
    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 = messageTimeoutMillis;
        }
    }

    // Poll.
    int result = POLL_WAKE;
    mResponses.clear();
    mResponseIndex = 0;

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

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    // 睡眠timeoutMillis,这时间是java层传进来的
    // eventItems保存了所有有变动的文件, eventCount表示有变动的文件个数,所以epoll效率
    // 比较高,不用遍历所有的数据了。
    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

    // No longer idling.
    mPolling = false;

    // Acquire lock.
    mLock.lock();

    // Rebuild epoll set if needed.
    if (mEpollRebuildRequired) {
        mEpollRebuildRequired = false;
        rebuildEpollLocked();
        goto Done;
    }

    // Check for poll error.发生错误了
    if (eventCount < 0) {
        if (errno == EINTR) {
            goto Done;
        }
        result = POLL_ERROR;
        goto Done;
    }

    // Check for poll timeout.
    // 没有就绪的文件
    if (eventCount == 0) {
        result = POLL_TIMEOUT;
        goto Done;
    }
    // 遍历所有的事件
    for (int i = 0; i < eventCount; i++) {
        int fd = eventItems[i].data.fd;
        uint32_t epollEvents = eventItems[i].events;
        if (fd == mWakeEventFd.get()) {
        // 如果是前面mWakeEventFd对应的文件,并且事件是EPOLLIN
            if (epollEvents & EPOLLIN) {
                // 把管道里面的数据读出来,清空管道
                awoken();
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
            }
        } else {
        // 其他类型的文件
            ssize_t requestIndex = mRequests.indexOfKey(fd);
            if (requestIndex >= 0) {
            // 通过addFd方法添加的
                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;
                // 加入到mResponses,但并没有马上执行
                pushResponse(events, mRequests.valueAt(requestIndex));
            } else {
                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
                        "no longer registered.", epollEvents, fd);
            }
        }
    }
Done: ;
    //开始处理消息
    // Invoke pending message callbacks.
    mNextMessageUptime = LLONG_MAX;
    // 先处理通过sendMessageXxx 添加的native消息
    while (mMessageEnvelopes.size() != 0) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
        if (messageEnvelope.uptime <= now) {
            // Remove the envelope from the list.
            // We keep a strong reference to the handler until the call to handleMessage
            // finishes.  Then we drop it so that the handler can be deleted *before*
            // we reacquire our lock.
            { // obtain handler
                sp<MessageHandler> handler = messageEnvelope.handler;
                Message message = messageEnvelope.message;
                mMessageEnvelopes.removeAt(0);
                mSendingMessage = true;
                mLock.unlock();
                // 调用handler处理消息
                handler->handleMessage(message);
            } // release handler

            mLock.lock();
            mSendingMessage = false;
            result = POLL_CALLBACK;
        } else {
            // The last message left at the head of the queue determines the next wakeup time.
            mNextMessageUptime = messageEnvelope.uptime;
            break;
        }
    }

    // Release lock.
    mLock.unlock();
    //再处理mResponses中的消息
    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;

            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            // callbackResult==0 移除Fd
            if (callbackResult == 0) {
                removeFd(fd, response.request.seq);
            }

            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            // 移除callback
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
    return result;
}

epoll_wait睡眠timeoutMillis时间,或者其他线程往消息队列中添加消息因为添加消息会往mWakeEventFd写入数据,会唤醒epoll。可以看到都会走到Done的代码,处理消息是先处理native的消息,再处理request类型的消息。

image.png