Message分为3中:普通消息(同步消息)、屏障消息(同步屏障)和异步消息。我们通常使用的都是普通消息,而屏障消息就是在消息队列中插入一个屏障(也是一个消息,这个消息会保存到当前Hanlder中,直到移除屏障消息),在屏障之后的所有普通消息都会被挡着,不能被处理。不过异步消息却例外,屏障不会挡住异步消息,因此可以这样认为:屏障消息就是为了确保异步消息的优先级,设置了屏障后,只能处理其后的异步消息,同步消息会被挡住,除非撤销屏障。

同步屏障

是通过MessageQueue的postSyncBarrier方法插入到消息队列的。

MessageQueue#postSyncBarrierprivate int postSyncBarrier(long when) {synchronized (this) {final int token = mNextBarrierToken++;//1、屏障消息和普通消息的区别是屏障消息没有tartget。final Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;Message prev = null;Message p = mMessages;//2、根据时间顺序将屏障插入到消息链表中适当的位置if (when != 0) {while (p != null && p.when <= when) {prev = p;p = p.next;}}if (prev != null) { // invariant: p == prev.nextmsg.next = p;prev.next = msg;} else {msg.next = p;mMessages = msg;//这里Handler中保存当前屏障消息}//3、返回一个序号,通过这个序号可以撤销屏障return token;}}

postSyncBarrier方法就是用来插入一个屏障到消息队列的,可以看到它很简单,从这个方法我们可以知道如下:

屏障消息和普通消息的区别在于屏障没有tartget,普通消息有target是因为它需要将消息分发给对应的target,而屏障不需要被分发,它就是用来挡住普通消息来保证异步消息优先处理的。
屏障和普通消息一样可以根据时间来插入到消息队列中的适当位置,并且只会挡住它后面的同步消息的分发。
postSyncBarrier返回一个int类型的数值,通过这个数值可以撤销屏障。
postSyncBarrier方法是私有的,如果我们想调用它就得使用反射。

如何发送异步消息

Handler有几个构造方法,可以传入async标志为true,这样构造的Handler发送的消息就是异步消息。不过可以看到,这些构造函数都是hide的,正常我们是不能调用的,不过利用反射机制可以使用@hide方法。

 /*** @hide*/public Handler(boolean async) {}/*** @hide*/public Handler(Callback callback, boolean async) { }/*** @hide*/public Handler(Looper looper, Callback callback, boolean async) {}

当调用handler.sendMessage(msg)发送消息,最终会走到:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);//把消息设置为异步消息}return queue.enqueueMessage(msg, uptimeMillis);}

可以看到如果这个handler的mAsynchronous为true就把消息设置为异步消息,设置异步消息其实也就是设置msg内部的一个标志。而这个mAsynchronous就是构造handler时传入的async。除此之外,还有一个公开的方法:

     Message message=Message.obtain();message.setAsynchronous(true);handler.sendMessage(message);

在发送消息时通过 message.setAsynchronous(true)将消息设为异步的,这个方法是公开的,我们可以正常使用。

屏障消息的工作原理

通过postSyncBarrier方法屏障就被插入到消息队列中了,那么屏障是如何挡住普通消息只允许异步消息通过的呢?我们知道MessageQueue是通过next方法来获取消息的。

 Message next() {//1、如果有消息被插入到消息队列或者超时时间到,就被唤醒,否则阻塞在这。nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {        Message prevMsg = null;Message msg = mMessages;//取出消息屏障if (msg != null && msg.target == null) {//2、遇到屏障  msg.target == nulldo {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());//3、遍历消息链表找到最近的一条异步消息}if (msg != null) {//4、如果找到异步消息if (now < msg.when) {//异步消息还没到处理时间,就在等会(超时时间)nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {//异步消息到了处理时间,就从链表移除,返回它。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;}//。。。。}}

可以看到,在注释2如果碰到屏障就遍历整个消息链表找到最近的一条异步消息,在遍历的过程中只有异步消息才会被处理执行到 if (msg != null){}中的代码。可以看到通过这种方式就挡住了所有的普通消息。

移除屏障

移除屏障可以通过MessageQueue的removeSyncBarrier方法:

//注释已经写的很清楚了,就是通过插入同步屏障时返回的token 来移除屏障


/*** Removes a synchronization barrier.** @param token The synchronization barrier token that was returned by* {@link #postSyncBarrier}.** @throws IllegalStateException if the barrier was not found.** @hide*/public void removeSyncBarrier(int token) {// Remove a sync barrier token from the queue.// If the queue is no longer stalled by a barrier then wake it.synchronized (this) {Message prev = null;Message p = mMessages;//找到token对应的屏障while (p != null && (p.target != null || p.arg1 != token)) {prev = p;p = p.next;}final boolean needWake;//从消息链表中移除if (prev != null) {prev.next = p.next;needWake = false;} else {mMessages = p.next;needWake = mMessages == null || mMessages.target != null;//消息屏障 删除}//回收这个Message到对象池中。p.recycleUnchecked();// If the loop is quitting then it is already awake.// We can assume mPtr != 0 when mQuitting is false.if (needWake && !mQuitting) {nativeWake(mPtr);//唤醒消息队列}}

同步屏障的应用

Android4.1之后增加了Choreographer(编舞者)机制,用于同 Vsync 机制配合,统一动画、输入和绘制时机。

ViewRootImpl的requestLayout开启绘制流程:

@Overridepublic void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();//检查是否在主线程mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。//重要函数scheduleTraversals();}}

Android 应用框架中为了更快的响应UI刷新事件在 ViewRootImpl.scheduleTraversals 中使用了同步屏障

void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;//设置同步障碍,确保mTraversalRunnable优先被执行mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//内部通过Handler发送了一个异步消息mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}

mTraversalRunnable 调用了 performTraversals 执行measure、layout、draw

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障。

Handler消息Message屏障消息相关推荐

  1. 【Android 异步操作】手写 Handler ( 消息队列 MessageQueue | 消息保存到链表 | 从链表中获取消息 )

    文章目录 一.MessageQueue 消息队列存储消息 二.MessageQueue 消息队列取出消息 三.消息队列完整代码 一.MessageQueue 消息队列存储消息 Message 链表 : ...

  2. 【Android 异步操作】Handler 机制 ( Android 提供的 Handler 源码解析 | Handler 构造与消息分发 | MessageQueue 消息队列相关方法 )

    文章目录 一.Handler 构造函数 二.Handler 消息分发 三.MessageQueue 消息队列相关函数 一.Handler 构造函数 一般使用 Handler 时 , 调用 Handle ...

  3. 【Android 异步操作】手写 Handler ( Handler 发送与处理消息 | Handler 初始化 | 完整 Handler 代码 )

    文章目录 一.Handler 发送与处理消息 ( 两大功能 ) 二.Handler 初始化 三.完整 Handler 代码 一.Handler 发送与处理消息 ( 两大功能 ) Handler 有两个 ...

  4. android 消息轮训,Android消息机制Handler,有必要再讲一次

    我们在日常开发中,总是不可避免的会用到 Handler,虽说 Handler 机制并不等同于 Android 的消息机制,但 Handler 的消息机制在 Android 开发中早已谙熟于心,非常重要 ...

  5. 远程调用服务(RPC)和消息(Message Queue)对比及其适用/不适用场合

    在阿里的平台技术部参与开发了Dubbo(远程调用服务)和Napoli(消息解决方案),又给网站应用支持这2个产品很长一段时间,了解了这2个产品的实现及应用对这两个产品的用法. 大部分情况下," ...

  6. Windows Azure NotificationHub+Firebase Cloud Message 实现消息推动(付源码)

    前期项目一直用的是Windows azure NotificationHub+Google Cloud Message 实现消息推送, 但是GCM google已经不再推荐使用,慢慢就不再维护了, 现 ...

  7. python 消息框架_消息框架message

    在网页应用中,我们经常需要在处理完表单或其它类型的用户输入后,显示一个通知信息给用户. 对于这个需求,Django提供了基于Cookie或者会话的消息框架messages,无论是匿名用户还是认证的用户 ...

  8. 什么是JMS(Java Message Service消息服务)

    JMS的定义 JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息 ...

  9. Message Bus - 消息总线

    Message Bus - 消息总线 Liferay的*消息总线(Message Bus)*是一种服务级API,组件可以用它来发送和接收消息.它提供了消息生产者(producers)和消费者(cons ...

最新文章

  1. ThinkPHP连接数据库
  2. linux守护实例有什么用,linux中的信号及进程守护的应用实例分享
  3. Spring框架bean的注解管理方法之一 使用注解生成对象
  4. java httpclient put_[工具类-HttpClientUtils]HttpClient之GET PUT DELETE POST
  5. mysql 5.6 安装_MySQL的安装,步骤详细
  6. 微软Build 2017首日主角AI 同时发布.NET Core 2.0 Preview 1
  7. html5语义化编程,HTML5常用的语义化标签
  8. Spark SQL and DataFrame Guide(1.4.1)——之DataFrames
  9. vb net excel 剪贴板 粘贴_excel表格操作: 图形和图表编辑技巧汇总(一)
  10. php使用 js格式解析,JavaScript解析JSON格式数据的方法示例
  11. 用vmware workstation做双机集群的详细过程(三)
  12. Openlayers GPS(度分秒)和经纬度坐标相互转换
  13. rails kaminari bootstrap-kaminari-views certified
  14. 中兴手机怎么与计算机连接网络连接不上,中兴手机怎么连接电脑
  15. 游山西村 陆游- 南宋
  16. 鸿蒙系统适配的电视,搭载鸿蒙系统的荣耀智慧屏电视适配app太少?网友:感觉上当了...
  17. 人工智能的发展对生活有什么影响?
  18. C语言,一个分号引发的问题:Run-Time Check Failure #2 - Stack around the variable ‘class1‘ was corrupted.
  19. 常用ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性归纳
  20. [原] 手擀寿面祝妈妈生日快乐

热门文章

  1. Python统计学10——时间序列分析自回归模型(ARIMA)
  2. ip地址转换数字函数 iton_IP 地址转换(在点分格式和数字之间相互转换)
  3. centos 虚拟机glibc升级_CentOS 6.5升级Glibc
  4. jmeter分布式环境搭建
  5. java .net webservice_Java客户端调用.NET的WebService实例
  6. 音视频技术开发周刊 | 295
  7. 基于python的网上订餐系统论文模板
  8. IEC 61851-1 协议
  9. 中国固定资产投资统计年鉴(1950-2021)
  10. KVM虚拟化技术及环境配置