Looper其实很简单

Posted xjz729827161

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Looper其实很简单相关的知识,希望对你有一定的参考价值。

每次提到这个looper,就有点感觉轻视的感觉,不就是个消息列队么。可是当别人问起,又不知道从何讲起,这次来一起捋一捋其中的道道,捋清楚后发现,还是很简单,哈哈。

Looper是一种单个线程中持续处理消息的的方式。

涉及到几个重要的类
Looper // 消息的调度
MessageQueue // 消息的存储
Message //消息本身,可以负载数据,也分为同步消息,异步消息
Handler //callback, runable, 消息的入口和处理地方

以主线程的Looper为例

创建Looper

ActivityThread.java

public static void main(String[] args)  
    
    Looper.prepareMainLooper();
    ...
    Looper.loop();


Looper.java

public static void prepareMainLooper() 
    prepare(false);
    synchronized (Looper.class) 
        //只允许调用一次
        if (sMainLooper != null) 
            throw new IllegalStateException("The main Looper has already been prepared.");
        
        //
        sMainLooper = myLooper();
    



private static void prepare(boolean quitAllowed) 
    //每个线程只能创建一次,每个线程如果调用后都拥有自己的looper
    //因为这个原因,衍生出一个类HandlerThread,是一个内部启动了Looper的线程
    if (sThreadLocal.get() != null) 
        throw new RuntimeException("Only one Looper may be created per thread");
    
    sThreadLocal.set(new Looper(quitAllowed));


这里需要插入一个知识点ThreadLocal

结束Looper

因为运行比较重要,就把结束放在前面说, 有两种方式quit和quitSafe,区别在于,quit会把队列中所有的消息出队,而quitSafe先只处理消息中大于当前时间的消息
Looper.java

public void quit()
public void quitSafe()

运行Looper,消息处理

Looper.java

//调用这个开始这个循环. emm..代码精简后就是这么简单。
//整体的思路就是不断的从队列里面去取消息, 取到消息就扔给消息的target进行派发,没有消息则阻塞
looper.loop() 
    ...
     for (;;) 
            Message msg = queue.next(); // might block
            if (msg == null) 
                // No message indicates that the message queue is quitting.
                return;
            
            try 
               //找到msg对应的handler来处理消息
                msg.target.dispatchMessage(msg);
             finally 
               
            
    
    ...

MessageQueue.java 取数据

让我们康康queue.next里面做了啥

Message next() 

    for (;;) 
        
        //这里会真正的进行阻塞,还会处理事件消息,里面是epoll_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;
                //msg.target == null的是屏障, 遇到屏障之后只取异步消息
                //这里就有一个问题,如果扔了一个屏障进来,不取消就ANR卡死了
                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) 
                    //整个队列的消息其实是以时间戳排序的,如果这个时间没有到
                    //执行的时间就继续等待,这种时间延后的就是延迟消息是平时使用的handler.postDelay之类的
                    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 
                        // 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.
                    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.
                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);
            
            //一个闲置处理,没有消息处理的时候,再触发逻辑
            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);
                    
                
            
    
    


上面的过程就完成了java端的looper。
如果不想去了解IO的派发过程,那么完全可以不用计较nativePollOnce(ptr, nextPollTimeoutMillis)里面的细节

可以理解为里面就做了一件事

//使用epoll的wait方法
  int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

队列中扔数据

MessageQueue.java

 boolean enqueueMessage(Message msg, long when) 
            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);
            
 

唤醒的动作

Looper.cpp

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

    uint64_t inc = 1;
    //重点就这一句,向epoll的唤醒文件描述符中,写入一个值
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) 
        if (errno != EAGAIN) 
            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
                    mWakeEventFd, strerror(errno));
        
    

总结下整个流程就是

  • 挂起唤醒: 通过epoll监听了一个 wakeFd文件, 如果没有消息的的时候可以调用epoll_wait挂起线程,
    有消息时向wakeFd中写入一个数值1即可唤醒epoll_wait的阻塞。
  • 消息: 消息分为异步和同步,绑定了一个target(Handler)来处理消息,
    如果target为null则是屏障消息, 屏障消息的作用是,所有的后入同步消息都在屏障消息后,除非屏障取消。
    这里一般用在invalidate触发的viewRootImpl performTravels中,保证UI刷新的优先级。
    异步消息也有一个使用的例子就是:ChoreographerpostCallbackDelayedInternal里有用到。
    里面额外补充一点的就是用到了重用的概念,有一个消息池,
    毕竟消息只是一个载体,没必要每次都反复生成对象,建议多使用Message.obtain()来创建消息
  • 消息队列: 入队消息, 按照消息按照时间戳进行排序,如果插入了非异步消息,并且当前已经阻塞了,则进行唤醒动作
  • Handler: 如果大家说怎么都没有看到说这个,是因为它其实不重要,message的target指向了一个handler. 然后会调用handler的disptachMessage. 这里也引入了一个知识点,handler的泄露,假设场景Activity创建匿名内部类Handler的实例来处理消息,则引用关系为 MessageQueue -> Message -> Handler -> Activity, 嗯,会泄露

OK , 以上的就有整个队列消息的取值,值入队, 阻塞,唤醒的动作。
当然也有头铁的大哥说,我就要了解下上面你说跳过的nativePollOnce里面的细节,其实也不复杂, epoll是可以监控多个文件的,除了监听一个给上层用来唤醒的文件外,还可以监听额外的文件。 实际中有用到的就是 监听的事件派发的socket文件的写入

android_os_MessageQueue.cpp

//里面有一个动态注册的,java的方法与native方法的映射

static const JNINativeMethod gMessageQueueMethods[] = 
    /* name, signature, funcPtr */
     "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit ,
     "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy ,
     "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce ,
     "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake ,
     "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling ,
     "nativeSetFileDescriptorEvents", "(JII)V",
            (void*)android_os_MessageQueue_nativeSetFileDescriptorEvents ,
;

//对应方法为 android_os_MessageQueue_nativePollOnce
//里面又引入了一个类NativeMessageQueue
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) 
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);


void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) 
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) 
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    


Looper.h (core/libutils/include/utils/)

 inline int pollOnce(int timeoutMillis) 
        return pollOnce(timeoutMillis, NULL, NULL, NULL);
    

Looper.cpp

//对于上层的pollOnce来说,除了timeoutMillis,后面的都NULL
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) 
    int result = 0;
    for (;;) 
    //每个request其实是封装的fd的监听
        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 != NULL) *outFd = fd;
                if (outEvents != NULL) *outEvents = events;
                if (outData != NULL) *outData = data;
                return ident;
            
        

        if (result != 0) 
#if DEBUG_POLL_AND_WAKE
            ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
            if (outFd != NULL) *outFd = 0;
            if (outEvents != NULL) *outEvents = 0;
            if (outData != NULL) *outData = NULL;
            return result;
        

        result = pollInner(timeoutMillis);
    




int Looper::pollInner(int timeoutMillis) 
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif

    // 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;
        
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
                this, mNextMessageUptime - now, timeoutMillis);
#endif
    

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

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

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];
    //重点来了。 epoll_wait,阻塞并支持超时
    int eventCount = epoll_wait(mEpollFd, 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;
        
        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
        result = POLL_ERROR;
        goto Done;
    

    // Check for poll timeout.
    if (eventCount == 0) 
#if DEBUG_POLL_AND_WAKE
        ALOGD("%p ~ pollOnce - timeout", this);
#endif
        result = POLL_TIMEOUT;
        goto Done;
    

    // Handle all events.
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
   //读取事件
    for (int i = 0; i < eventCount; i++) Looper其实很简单

Looper其实很简单

Android-Handler同步屏障

Android图形系统(十一)-Choreographer

java中的简单屏障同步

幸福其实很简单