一、概述

简单理解为 异步消息插队并优先执行。

场景:排队买票

先来了一个普通用户来排队,买完票走了。

后面又来了一个VIP用户A来买票 就一直站在卖窗口这里 也不走(ps:添加屏障 )

紧接者又来了一个普通用户C,再后面又来了VIP用户B

VIP A 对VIP B 说,哥们不要排队直接来窗口买票,VIP B买完票走了,VIP A 被 某个哥们叫走了(移除屏障)

这个时候终于轮到普通用户C买票了。

二、系统应用

简单的来说就是优于事件回调执行,为了做一些优先级更高的操作 比如 视图刷新。当一个Handler消息来时 会优先于执行同步屏障消息事件。

以便系统底层可以做一些比上层业务更加重要的消息事件 ,所以 这个方法 被注解成hide 也是系统给自己开了一道后门。不然的话把方法公开给应用去使用,那么很可能把系统卡成翔而导致掉帧。

申请VSYNC信号前加入屏障,保证被优先执行

这里的handler就是主线程的handler

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

//设置同步障碍,确保mTraversalRunnable优先被执行

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

//内部持有Handler关联主looper, 然后通过Handler发送了一个异步消息到主线程messageQueue

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

。。。。

}

}

以上说明申请VSYNC信号非常重要,如果申请VSYNC不及时会造成屏幕不流畅卡顿现象,所以说Android是不允许在主线程做耗时操作的一个重要原因,因为当前一个消息正在onHandlerMesage中(main)做耗时操作,那么VSYNC申请会处于一个等待状态 造成屏幕无法在16.6ms内刷新(一般来说屏幕刷新频率 主流为60Hz ,也就是16.6ms刷新一次)。

回调后:

自己被执行了才移除屏障

进行View的绘制流程

void doTraversal() {

if (mTraversalScheduled) {

mTraversalScheduled = false;

//移除消息屏障

mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

if (mProfile) {

Debug.startMethodTracing("ViewAncestor");

}

performTraversals();

if (mProfile) {

Debug.stopMethodTracing();

mProfile = false;

}

}

}

三、源码实现

3.1 Message分类

Handler中的Message可以分为两类:同步消息、异步消息。消息类型可以通过以下函数得知

//Message.java

public boolean isAsynchronous() {

return (flags & FLAG_ASYNCHRONOUS) != 0;

}

一般情况下这两种消息的处理方式没什么区别,只有在设置了同步屏障时才会出现差异。

3.2 MessageQueue的特殊处理

3.2.1 MessageQueue.postSyncBarrier

private int postSyncBarrier(long when) {

// Enqueue a new sync barrier token.

// We don't need to wake the queue because the purpose of a barrier is to stall it.

synchronized (this) {

final int token = mNextBarrierToken++;

final Message msg = Message.obtain();

msg.markInUse();

msg.when = when;

msg.arg1 = token;

Message prev = null;

Message p = mMessages;

if (when != 0) {

while (p != null && p.when <= when) {

prev = p;

p = p.next;

}

}

if (prev != null) { // invariant: p == prev.next

msg.next = p;

prev.next = msg;

} else {

msg.next = p;

mMessages = msg;

}

return token;

}

}

该函数仅仅是创建了一个Message对象并加入到了消息链表中。乍一看好像没什么特别的,但是这里面有一个很大的不同点是 该Message没有target, 这也就意味着被looper取出后不经过handler执行。

和普通消息的差异:没有targer:Handler

我们通常都是通过Handler发送消息的,Handler中发送消息的函数有post***、sendEmptyMessage***以及sendMessage***等函数,而这些函数最终都会调用enqueueMessage函数:可以看到enqueueMessage为msg设置了target字段

//Handler.java

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

//...

return queue.enqueueMessage(msg, uptimeMillis);

}

然后在looper中转发给Handler处理:

public static void loop() {

final Looper me = myLooper();

final MessageQueue queue = me.mQueue;

for (;;) {

//取出消息,没有消息则阻塞

Message msg = queue.next();

msg.target.dispatchMessage(msg);

}

}

和普通消息的差异:空消息,不唤醒线程

注意的是 添加消息屏障并没有调用 nativeWake(mPtr) 来唤醒线程。

而通过enqueueMessage 消息是有去调用nativeWake(mPtr) 来唤醒线程的。(ps:当主线程阻塞状态 才会触发nativeWake)

很好理解:屏障只是为了后续加入的异步信息,如果没有信息就不需要唤醒线程,有信息自然就会走enqueueMessage唤醒

3.2.2 MessageQueue.next

Message next() {

//...

int pendingIdleHandlerCount = -1; // -1 only during first iteration

int nextPollTimeoutMillis = 0;

for (;;) {

//...

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 while循环遍历消息链表

// 跳出循环时,msg指向离表头最近的一个“非同步消息”,没有就会为null

do {

prevMsg = msg;

msg = msg.next;

} while (msg != null && !msg.isAsynchronous());

}

if (msg != null) {

if (now < msg.when) {

//...

} else {

// Got a message.

mBlocked = false;

if (prevMsg != null) {

//将msg从消息链表中移除

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;

}

//...

}

//...

}

}

当设置了同步屏障之后,next函数将会忽略所有的同步消息,返回异步消息。

也就是说,如果第一条消息就是屏障,那么就往后遍历 看看有没有异步消息

有 :再看离这个消息触发 还有多久,设置一个超时继续休眠

没有:就继续休眠,等待被别人唤醒,此时该屏障一直存在在消息队列头部

换句话说就是,设置了同步屏障SyncBarrier之后,Handler只会处理isAsynchronous异步消息。

再换句话说,同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。

3.2.3 MessageQueue.removeSyncBarrier移除屏障

public void removeSyncBarrier(int token) {

// Remove a sync barrier token from the queue.

//....省略.......移除队列中barrier的token消息

//唤醒线程

if (needWake && !mQuitting) {

nativeWake(mPtr);

}

}

}

移除一个消息屏障,做了以下几件事:

1.移除次序列号的token消息

2.如果主线程是阻塞状态,则唤醒线程

3.3 Handler发送异步信息

如何发送异步消息

通常我们使用Handler发消息时,这些消息都是同步消息,如果我们想发送异步消息,那么在创建Handler时使用以下构造函数中的其中一种(async传true)

public Handler(boolean async);

public Handler(Callback callback, boolean async);

public Handler(Looper looper, Callback callback, boolean async);

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this; //target 是不会为null的

if (mAsynchronous) {// 默认为false ,消息默认是被标记为同步(普通)消息

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

然后通过该Handler发送的所有消息都会变成异步消息

四、总结

一般屏障是和异步 是一起配合使用的,直到调用removeSyncBarrier 那么后面的普通消息才有机会执行;

被 Message : p.target 的被标记为屏障消息。

被setAsynchronous(true) 为异步消息 ;

当我们利用handler 发送消息的时候,根据Handler的属性判断是否发送异步信息:

Handler:postSyncBarrier 和 removeSyncBarrier 方法都是被@hide ,是无法直接调用的,需通过反射来使用;

postSyncBarrier 不会唤醒线程, removeSyncBarrier 会唤醒线程(当队列里面有消息时);

具体流程如下:

只要遍历到一个屏障消息 ,那么相当于再这个时间添加了屏障(这里不会主动唤醒线程) ,那么后面入队的异步消息 都优先执行

没有则一直阻塞,如果这个时候一个普通消息sendMessageDelayed(getPostMessage®, 0)入队 会触发唤醒线程。

如果队列里面有异步消息 则取出此异步消息返回 然后继续阻塞线程 ,直到移除屏障消息(这里才会触发唤醒线程)。

如果队列里面没有异步消息则继续阻塞 ,直到调用removeSyncBarrier移除屏障那么才会取出这个普通消息 返回。

参考文献

android同步方法和对象的区别是什么,(4.1.10.8)Android Handler之同步屏障机制(sync barrier)...相关推荐

  1. Android:同步屏障的简单理解和使用

    同步屏障的简单理解和使用 1.背景 2.何为同步屏障? 2.1. 发送屏障消息--postSyncBarrier 2.2.发送异步消息 2.3.处理消息 2.4.移除屏障消息--removeSyncB ...

  2. Xposed: 勾住(Hook) Android应用程序对象的方法,实现AOP

    Xposed Xposed能够勾住(Hook) Android应用程序对象的方法,实现AOP,一个简单的例子: public class WebViewHook implements IXposedH ...

  3. android parcelable 对象,Android实现Parcelable对象序列化的实例

    Android实现Parcelable对象序列化的实例 bundle.putParcelable可以实现传递对象,但是这个对象的类必须实现Parcelable接口才能够使用. 下面是一个简单的在Act ...

  4. android SharedPreferences 存储对象

    原文地址为: android SharedPreferences 存储对象 我们知道SharedPreferences只能保存简单类型的数据,例如,String.int等. 如果想用SharedPre ...

  5. Android so库文件的区节section修复代码分析

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78818917 一.Android so库文件的节表secion修复方案整理 1. ...

  6. JVM内存模型和性能调优:JVM内存分配与回收:Minor GC后存活的对象Survivor区放不下- 第26篇

    Minor GC后存活的对象Survivor区放不下,这种情况会把存活的对象部分挪到老年代,部分可能还会放在Survivor区. (1)当我们的代码中有allocation1和allocation2 ...

  7. android bundle 对象,Android Bundle传递对象

    首先Android的Bundle是可以传递对象的.我们可以用Bundle b = new Bundle():b.putSerializable("key", 对象引用); 但是这样 ...

  8. 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制

    第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...

  9. 认识Android(常用布局,控件,四大组件,动画,自定义控件及异常消息处理机制)

    目录 一.布局 1.LinearLayout(线性布局): 2.相对布局(RelativeLayout) 3.GridLayout(网格布局) 4.FrameLayout(帧布局): 二.控件 1.T ...

最新文章

  1. Yii2.0 RESTful API 之版本控制
  2. 那些做了多少次错了多少次的题目
  3. 用Javascript隐藏超级链接的真实地址
  4. Oracle数据库的下载和安装
  5. 【unity基础系列】1、unity Texture Type设置为Advanced时纹理的格式列表
  6. mac用vscode打开html,Mac 命令行打开VsCode
  7. 简易拨号器iCall
  8. 【数论】能量采集(P1447)
  9. CSS3实现的响应式字体:自适应视图窗口大小的新单位
  10. 京东供应链金融科技推出“采购融资”服务 最高100万无抵押融资
  11. 如何使用SSH连接到远程MySQL服务器
  12. 如果你需要从不同的服务器(不同域名)上获取数据就需要使用跨域 HTTP 请求...
  13. 【mysql】join的用法和修改数据表
  14. java中model的意思_开发中model,entity和pojo的区别
  15. 移动端h5框架自适应_最佳移动端h5自适应rem适配方案
  16. latex参考文献生成双语对照文献表
  17. 特种浓缩分离:染料纳滤膜脱盐浓缩技术
  18. C++入门教程(四十二):函数参数使用引用
  19. 【NOIP 2017】Day2 T3 列队
  20. 北航计算机考研小黑书,不会真有人以为四川大学分数低吧?川大最高分比北航南大最高分还高...

热门文章

  1. 数据结构与算法的分析 —— 渐进复杂度(三个记号)
  2. Python 爬虫 —— 使用 pandas
  3. 机器学习的数学基础(一)—— 期望、方差、协方差与相关系数
  4. dedeindex php不显示_dede去掉(禁止)首页index.html默认访问 最终显示index.php
  5. python安装教程-01-Python安装教程与特色介绍-小白python入门基础
  6. python怎么读取excel-python怎么读取excel表格
  7. python和java哪个好学-Python和Java对比,全面解读哪个语言最赚钱,前景最好?
  8. 0基础学python要多久-零基础学习Python开发需要多长时间?
  9. 济南python工资一般多少-2020年济南学python好点的学校
  10. 学python买什么书-关于 Python 的经典入门书籍有哪些?(python三大经典书)