MessageQueue原理分析(1)

每次使用Handler发送一个Message的时候,最终会先调用MessageQueue的enqueueMessage方法将Message方法放入到MessageQueue里面,最后会调用Handler的mQueue的enqueueMessage方法。

MessageQueue类内部实现了两个Interface,一个静态内部类。

  • 接口IdleHandler在消息队列没有消息时使用,处理poll状态时的动作
  • 接口OnFileDescriptorEventListener在相应的文件状态改变(可读,可写,有错误)时被使用
  • 静态内部类FileDescriptorRecord,记录相应文件状态改变时的监视器OnFileDescriptorEventListener,在被native方法调用的dispatchEvents方法里被调用,执行监视器

在android中,Handler负责发送message到messageQueue,Handler包括looper,looper中创建的MessageQueue。Looper在新的线程中需要先prepare,然后启动loop循环,loop循环就是不断的从messageQueue中取出待处理message。
首先 enqueueMessage

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            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;
    }

整个enqueueMessage方法的过程就是先持有MessageQueue.this锁,然后将Message放入队列中,放入队列的过程是:

  1. 如果队列为空,或者当前处理的时间点为0(when的数值,when表示Message将要执行的时间点),或者当前Message需要处理的时间点先于队列中的首节点,那么就将Message放入队列首部,否则进行第2步。

  2. 遍历队列中Message,找到when比当前Message的when大的Message,将Message插入到该Message之前,如果没找到则将Message插入到队列最后。

  3. 判断是否需要唤醒,一般是当前队列为空的情况下,next那边会进入睡眠,需要enqueue这边唤醒next函数。后面会详细介绍

接下来是取出message的方法 next

    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.
        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();
            }

            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.
                        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);
            }

            // 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;
        }
    }

整个next函数的主要是执行步骤是:

  • step1: 初始化操作,如果mPtr为null,则直接返回null,设置nextPollTimeoutMillis为0,进入下一步。

  • step2: 调用nativePollOnce, nativePollOnce有两个参数,第一个为mPtr表示native层MessageQueue的指针,nextPollTimeoutMillis表示超时返回时间,调用这个nativePollOnce会等待wake,如果超过nextPollTimeoutMillis时间,则不管有没有被唤醒都会返回。-1表示一直等待,0表示立刻返回。

  • step3: 获取队列的头Message(msg),如果头Message的target为null,则查找一个异步Message来进行下一步处理。当队列中添加了同步Barrier的时候target会为null。

  • step4: 判断上一步获取的msg是否为null,为null说明当前队列中没有msg,设置等待时间nextPollTimeoutMillis为-1。实际上是等待enqueueMessage的nativeWake来唤醒,执行step4。如果非null,则下一步

  • step5: 判断msg的执行时间(when)是否比当前时间(now)的大,如果小,则将msg从队列中移除,并且返回msg,结束。如果大则设置等待时间nextPollTimeoutMillis为(int) Math.min(msg.when - now, Integer.MAX_VALUE),执行时间与当前时间的差与MAX_VALUE的较小值。执行下一步

  • step6: 判断是否MessageQueue是否已经取消,如果取消的话则返回null,否则下一步

  • step7: 运行idle Handle,idle表示当前有空闲时间的时候执行,而运行到这一步的时候,表示消息队列处理已经是出于空闲时间了(队列中没有Message,或者头部Message的执行时间(when)在当前时间之后)。如果没有idle,则继续step2,如果有则执行idleHandler的queueIdle方法,我们可以自己添加IdleHandler到MessageQueue里面(addIdleHandler方法),执行完后,回到step2。

native方法

// 定义在 frameworks/base/core/jni/android_os_MessageQueue.cpp 中,在构造函数中被调用,
// 它创建了一个native层的Looper。Looper的源码在system/core/libutils/Looper.cpp。
// Looper通过epoll_create创建了一个mEpollFd作为epoll的fd,并且创建了一个mWakeEventFd,
// 用来监听java层的wake,同时可以通过Looper的addFd方法来添加新的fd监听。
private native static long nativeInit();

private native static void nativeDestroy(long ptr);

// 这个方法的native层方法最终会调用Looper的pollOnce,具体实现在下面给出
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/

// 调用的是Looper.cpp中的wake方法
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);

 

int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
   int result = 0;
   for (;;) {
       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];
   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, errno=%d", 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++) {
       int fd = eventItems[i].data.fd;
       uint32_t epollEvents = eventItems[i].events;
       if (fd == mWakeEventFd) {
           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) {
               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: ;

   // Invoke pending message callbacks.
   mNextMessageUptime = LLONG_MAX;
   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();

#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
               ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
                       this, handler.get(), message.what);
#endif
               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();

   // 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;
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
           ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
                   this, response.request.callback.get(), fd, events, data);
#endif
           // Invoke the callback.  Note that the file descriptor may be closed by
           // the callback (and potentially even reused) before the function returns so
           // we need to be a little careful when removing the file descriptor afterwards.
           int callbackResult = response.request.callback->handleEvent(fd, events, data);
           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.
           response.request.callback.clear();
           result = POLL_CALLBACK;
       }
   }
   return result;
}

这是一个很大的方法,具体过程如下:

  1. 调用epoll_wait方法等待所监听的fd的写入,可参考epoll
  2. 如果epoll_wait返回了,那么可能是出错返回,可能是超时返回,可能是有事件返回,如果是前两种情况跳转到Done处。
  3. 否则,会判断事件是否是mWakeEventFd(唤醒的时候写入的文件)做不同处理。如果是,则调用awoken方法,读取Looper.wake写入的内容,否则通过pushResponse读取,并将内容放入 Response中
  4. 处理NativeMessageQueue的消息,这些消息是native层的消息
  5. 处理pushResponse写入的内容。

SyncBarrier 同步消息屏障

当消息队列的第一个Message的target的时候,表示它是一个SyncBarrier,它会阻拦同步消息,而选择队列中第一个异步消息处理,如果没有则会阻塞。表示第一个Message是SyncBarrier的时候,会只处理异步消息,这时异步消息优先被处理,保证了用户体验,是在scheduleTraversal方法中被设置的

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

 //Choreographer.postCallback会调用该方法
 private void postCallbackDelayedInternal(int callbackType,
         Object action, Object token, long delayMillis) {
             ... 
             Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
             msg.arg1 = callbackType;
             msg.setAsynchronous(true);//设置为异步,不被屏障影响
             mHandler.sendMessageAtTime(msg, dueTime);
             ...
 }

同时我们看到在 doTraversal中会removeSyncBarrier,doTraversal 会调用performTraversals,也就是measure、layout、draw,而doTraversal是在 TraversalRunnable 中被调用的。

因此可以得出结论,当系统开始绘制ViewTree时将会向UI线程的消息队列中插入同步屏障以阻塞有关UI消息的执行
mChoreagrapher.postCallback 方式实际上调用的仍然是Handler.sendMessageAtTime, 只不过其带有的 mTraversalRunnable 的Message被设置了setAsynchronous异步消息,同步屏障不会影响其执行。也就是说ViewRootImpl 通过Handler机制中的同步屏障保证View绘制优先执行

场景

当在activity启动时,需要加载界面,其中有一个图片要加载到一个ImageView中,为了提高效率,同时执行界面和图片的加载,那图片加载必须要在界面加载结束后才可以设置到ImageView中,此时可以通过消息屏障来实现。

 

IdleHandler

MessageQueue中有一个接口很少被提及和使用,那就是IdleHandler。

但是其中有一个接口很少被提及和使用,那就是IdleHandler。

IdleHandler

一、源码解析

根据注释我们很清楚的知道,这个接口方法是在消息队列消息队列全部处理完成后或者是在阻塞的过程中等待更多的消息的时候调用的,返回值绝对了处理一次后是否保存这个接口。虽然注释很清楚,但是我们更多的应该是要知道他是怎么处理的,所以看源码:

先看下接口的添加和删除:

添加接口

删除接口

从上面所贴出的源码中我们可以得到两点:

1、添加和删除是线程安全的

2、通过查找可以得知mIdleHandlers是一个ArrayList,因此这个接口是可以重复添加的,不必担心被替换问题

接下来我们看看接口是如何被调用的,通过源码查找,我们知道了它是在next()方法中调用的,同时我们也知道,这个方法也是消息队列用来循环消息的地方,由于方法比较长,我就只贴部分关键源码:

其实这部分源码注释都已经很清楚了

第一部分应该算是条件判断,首先判断的是是不是第一次,pendingIdleHandlerCount的初始化为-1,其次判断的是有没有IdleHandler接口,如果没有的话就是阻塞

第二部分是接口处理,主要是接口的调用以及判断是不是接口只运行一次就不需要了,最后是设置判断的条件

二、系统源码中的IdleHandler使用

通过上面的源码分析可以找到,他是空闲时调用,这一定是一个非常有用的接口。于是在系统源码中查找它的使用,发现了一点小小的惊喜,在这里分享给大家

在对源码的学习中,我发现了IdleHandler的踪迹,他出现的位置也是很关键的位置,在ActivityThread中,学习过源码的同学都知道,这是一个非常重要的类,因为其实这才算是一个程序的入口,额,有点远了,有兴趣的同学可以自己去看下这个类,不多说,上源码

上面的代码很简单,主要是添加和移除接口,以及接口实现,但是接口实现的时候有个小惊喜啊,看方法名称就很意外doGcIfNeeded(),这个地方涉及到回收。大家都知道系统会自动回收,但是什么时候回收,哪个地方回收的,一直都很模糊,现在总算知道一点了,但是这仅仅是一个方面的回收,不要当成全部

回收

这个也很简单了,主要是2次回收有最小时间间隔,就不多说了

Tips:

如果没看过ActivityThread可能不知道上面的Looper.myQueue()指的是哪个Looper这里给大家说明下,还是先上源码:

ActivityThread.java

使用过Looper的人应该都很明白这个,从代码中可以看出,这个地方准备的是主线程,所以回收添加的Looper是MainLooper,另外大家不会忘了main方法吧:-D

 

三、其他源码中的使用

请大家原谅我年轻,阅读的开源代码比较少,也可能是其他的开源项目中没有这样的需求,所以只在Glide中看到过这个接口的使用,Google不愧为最了解源码的啊,源码贴给大家

Engine.java

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值