每次使用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.nextprev.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 iterationint 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 handlerboolean 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_WAKEALOGD("%p ~ pollOnce - returning signalled identifier %d: ""fd=%d, events=0x%x, data=%p",this, ident, fd, events, data);
#endifif (outFd != NULL) *outFd = fd;if (outEvents != NULL) *outEvents = events;if (outData != NULL) *outData = data;return ident;}}if (result != 0) {
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - returning result %d", this, result);
#endifif (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_WAKEALOGD("%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_WAKEALOGD("%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_WAKEALOGD("%p ~ pollOnce - timeout", this);
#endifresult = POLL_TIMEOUT;goto Done;}// Handle all events.
#if DEBUG_POLL_AND_WAKEALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endiffor (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 handlersp<MessageHandler> handler = messageEnvelope.handler;Message message = messageEnvelope.message;mMessageEnvelopes.removeAt(0);mSendingMessage = true;mLock.unlock();#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKSALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",this, handler.get(), message.what);
#endifhandler->handleMessage(message);} // release handlermLock.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_CALLBACKSALOGD("%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

MessageQueue原理分析(1)相关推荐

  1. android 实例源码解释,Android Handler 原理分析及实例代码

    Android Handler 原理分析 Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题 今天就为大家详细剖析下Handler的原理 Handler使 ...

  2. Android JNI原理分析

    引言:分析Android源码6.0的过程,一定离不开Java与C/C++代码直接的来回跳转,那么就很有必要掌握JNI,这是链接Java层和Native层的桥梁,本文涉及相关源码: frameworks ...

  3. Handler原理分析

    Handler的原理分析这个标题,很多文章都写过,最近认真将源码逐行一字一句研究,特此也简单总结一遍. 首先是Handler整个Android消息机制的简单概括: 分三部分对消息机制的整个流程进行阐述 ...

  4. Android--Handler使用应运及消息机制处理原理分析

    最近开通了一个小微博,欢迎大家关注,每天分享一些上班路上看的小知识点 点击打开链接 一.Handler是什么 ? handler是android给我们提供的一套用来更新UI的一套机制,也是一套消息处理 ...

  5. Android 6.0 JNI原理分析 和 Linux系统调用(syscall)原理

    JNI原理 引言:分析Android源码6.0的过程,一定离不开Java与C/C++代码直接的来回跳转,那么就很有必要掌握JNI,这是链接Java层和Native层的桥梁,本文涉及相关源码: fram ...

  6. 分布式专题-分布式消息通信之ActiveMQ03-ActiveMQ原理分析(下)

    目录导航 前言 unconsumedMessage源码分析 异步分发的流程 同步分发的流程 消费端的PrefetchSize及优化 原理剖析 prefetchSize 的设置方法 总结 消息的确认过程 ...

  7. java signature 性能_Java常见bean mapper的性能及原理分析

    背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换.赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils.BeanCopier.Dozer. ...

  8. Select函数实现原理分析

    转载自 http://blog.chinaunix.net/uid-20643761-id-1594860.html select需要驱动程序的支持,驱动程序实现fops内的poll函数.select ...

  9. spring ioc原理分析

    spring ioc原理分析 spring ioc 的概念 简单工厂方法 spirng ioc实现原理 spring ioc的概念 ioc: 控制反转 将对象的创建由spring管理.比如,我们以前用 ...

最新文章

  1. 关于Linux下进程创建的相关知识
  2. jenkins docker 安装_docker 安装 Jenkins
  3. PYTHON * 和**的用法
  4. 简约之美Jodd-http--深入源码理解http协议
  5. C语言面试基础知识整理
  6. wxWidgets:常用对话框
  7. 云栖发布|阿里云消息队列 RocketMQ 5.0:消息、事件、流融合处理平台
  8. 表格缓存问题_缓存常见问题,一网打尽哦!
  9. 使用Docker-数据卷挂载案例1
  10. 进击吧!Blazor!第一期回顾
  11. 【转发】响应式Web设计?怎样进行?
  12. 基于ruby环境搭建Redmine
  13. css 下拉 小箭头
  14. Word 分节符的删除(不改变文章格式)
  15. linux无法访问443端口,无法监听EC2上的https端口443(Amazon Linux)
  16. css3 中的calc用法
  17. 六大学习趋势正重塑在线教育产业-网络线上教学
  18. 微信支付小程序支付和APP支付
  19. android百度地图小人头像怎么做,出包女王村雨静-小静-Murasame Oshizu-头像图片-资料介绍-acg人物点评...
  20. 【笔记/后端】谷粒商城基础篇

热门文章

  1. C#读写基恩士PLC 使用TCP/IP 协议 MC协议
  2. 船舶驾驶虚拟仿真模拟训练系统软件
  3. Pjsip加入G729编码的方法
  4. OpenCV安装成功,但是无法导入 cv2 的指定模块 -- Ubuntu Anaconda 环境一键安装 opencv
  5. 2023年国开《ERP原理与应用》实验1-5学习行为表现
  6. VLAN配置(IN8000系列交换机)
  7. ySQL字符串函数:字符串截取
  8. 常见品牌、型号路由器对应的断开地址,如何在博客蜘蛛软件中设置路由路径
  9. 【空间天气】中高层大气
  10. 【回答问题】ChatGPT上线了!如何使用控制算法或动力学模型控制PreScan搭建的仿真环境及相关车辆的运动状态?