Android从屏幕刷新到View的绘制(三)之Handler异步消息与同步屏障
0. 相关分享
Android从屏幕刷新到View的绘制(一)之 Window、WindowManager和WindowManagerService之间的关系
Android从屏幕刷新到View的绘制(二)之Choreographer、Vsync与屏幕刷新
1. 相关类
Handler
Handler中维护着它所在的线程的Looper,或者由用户设定的Looper。Looper分发消息Message的时候,会根据Message中设定的target,也就是Handler实例,来决定由哪个Handler来处理这个Message消息。通常我们通过重写handleMessage()方法来自定义对Message的处理。
Message
消息实体,内部的实例数据包括了:这个Message的code编号信息what,让处理者知道这是什么类型的消息,它所承载的数据obj,表示合适执行的when,表示分发给哪个Handler的target等。
它是单向链表的结构,next变量指向了下一个Message。同时对Message的对象池缓存,也定义在了Message类中,是最大长度为50的单向链表sPool。可以通过obtain()来获取合适的可复用的Message。
MessageQueue
它是访问Message的入口,管理着Message链表,与Looper一一对应,在Looper实例化的时候进行的MessageQueue实例化。提供了对Message的管理操作,例如next()获取下一个Message、removeMessages()删除指定的Message、以及我们本文要谈到的添加删除同步屏障消息。
Looper
它是一个循环主体,每个线程最多只能有一个Looper,可以通过sThreadLocal来获取Thread私有的Looper,也可以通过getMainLooper()来获取主线程的Looper从而实现与主线程的通信,或者叫做线程切换。它的loop()方法打开了死循环,循环不断地向MessageQueue索取下一个可用的Message,阻塞获取的功能在MessageQueue中实现。他能获取到同步消息、异步消息,具体线下要分发什么消息由MessageQueue来决定。
2. 核心方法
2.1 MessageQueue.postSyncBarrier()
添加同步屏障,我们可以通过MessageQueue的postSyncBarrier()来实现:
//MessageQueue.java
public int postSyncBarrier() {return postSyncBarrier(SystemClock.uptimeMillis());
}private int postSyncBarrier(long when) {synchronized (this) {//这个token用来表示这个同步屏障是谁,可以借此用来remove同步屏障final int token = mNextBarrierToken++;//复用或者获取一个Messagefinal Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;//但不给这个Message设置target,以此表征它是同步屏障Message prev = null;Message p = mMessages;if (when != 0) {while (p != null && p.when <= when) {prev = p;p = p.next;}}//它将会插入到还未到执行时间的Message之前。但已经到了执行时间的,不会被同步屏障挡住。if (prev != null) { // invariant: p == prev.nextmsg.next = p;prev.next = msg;} else {msg.next = p;mMessages = msg;}return token;}
}
在工程中,我们想要添加同步屏障,可以通过让MessageQueue来postSyncBarrier()的方式:
looper.getQueue().postSyncBarrier();
需要的是,就算加了同步屏障,也不代表异步消息能够立即执行,因为添加同步屏障的时候,会跳过那些执行时间已经到达,需要立即执行的任务。同步屏障Message只会插入在立即执行的Message之后,非立即执行的Message之前。
所以即使收到了Vsync,分发了performTraversals()的异步任务,也不一定能够立即进行View的绘制流程。如果主线程有耗时操作,就可能引发跳帧。我们这里需要知道一个知识,并不是调用完View的绘制流程就立刻刷新屏幕的,只是对一个缓存进行数据修改,等到下一次Vsync到来的时候,SurfaceFlinger才会进行渲染刷新。
如果分发的绘制消息超时了,没有执行对缓存的更新,那么SurfaceFlinger绘制的仍然是之前的缓存,所以会表现出画面卡顿,因为SurfaceFlinger只负责把缓存取出来进行渲染,而把数据写入缓存的任务由于前面Message的任务超时,导致没有及时将数据写入,所以屏幕没有变化,也就是画面卡顿。MessageQueue拿到这个异步绘制消息的时候会判断执行时间,发现超时了,说明前面卡顿导致了跳帧,就会提示下面这样的log打印消息:
Choreographer: Skipped 599 frames! The application may be doing too much work on its main thread.
2.2 MessageQueue.next()
同步屏障的核心原理,其实就是MessageQueue在获取下个可用Message的时候,会判断获取到的是不是一个同步屏障,如果是,则继续往后找,找到可用的异步消息,将其返回出去。
Message next() {for (;;) {//阻塞获取nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.target == null) {//如果msg的target为null,就认为是同步屏障do {prevMsg = msg;msg = msg.next;//遍历跳过它之后所有同步消息} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {//如果将要获取得到的消息执行时间还没到,则跳过,进入下一次循环阻塞,并设置阻塞时间!nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {//如果拿到的Message没问题,时间到了,也不是同步屏障,那么就把它从Message链表中移除,并返回出去mBlocked = false;if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;//标记为正在使用msg.markInUse();return msg;}} else {//如果message=null,也就是没有获取到Message,那么就进入到生产消费者模型的无限期等待情况,直到有Message进入,进行唤醒。nextPollTimeoutMillis = -1;}//退出looper的处理if (mQuitting) {dispose();return null;}}
}
2.3 发送异步消息
在实例化Handler的时候,可以将该Handler设为专门发送异步消息的:
public Handler(@Nullable Callback callback, boolean async) {...mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;//异步标志mAsynchronous = async;
}
后续由这个Handler发送Message的时候,会把所有由它加入MessageQueue的Message打上异步标记:
//Handler
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {//将Handler赋值给Message的target变量msg.target = this;//mAsynchronous为false,为同步消息if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}
当然,普通Handler也可以发送异步消息,这就需要我们手动将Message设置为异步消息,核心就是:
msg.setAsynchronous(true);
我们知道 Choreographer 中使用的都是异步消息。
相关文章:
Android从屏幕刷新到View的绘制(三)之Handler异步消息与同步屏障
0. 相关分享 Android从屏幕刷新到View的绘制(一)之 Window、WindowManager和WindowManagerService之间的关系 Android从屏幕刷新到View的绘制(二)之Choreographer、Vsync与屏幕刷新 1. 相关类 Handler Handler中维护着它所在的…...
最新版axios@1.3.x取消请求-AbortController-初体验-番茄出品
最新版axios1.3.x取消请求-AbortController-初体验-番茄出品 start 前文提到,axios 中的取消请求,包含两种方式: AbortController;CancelToken; 上篇文章讲解了 CancelToken,今天这篇文章来了解一下 Abor…...
Git的简述
Git 文章目录GitGit概述版本控制工具集中式管理控制工具分步式管理控制工具控制机制Git和代码托管中心安装Git软件Git常用命令Git概述 Git是一个免费的、开源的分步式版本控制系统,可以快速的处理从小型到大型的各种项目 Git 易于学习,占地面积小&…...
webpack实战,手写loader和plugin
序言 对于 webpack 来说, loader 和 plugin 可以算是需求程度最为广泛的配置项了。但是呢,单单止步于配置可能还不够。如果我们自己有时候想要 diy 一个需求,但是 webpack 又没有相关的 loader 和 plugin 。那这个时候我们可能就得开始造点轮…...
STM32CubeMX按键模块化 点灯
本文代码使用 HAL 库。 文章目录前言一、按键原理图二、CubeMX 创建工程三、代码讲解:1. GPIO的输入HAL库函数:2. 消抖:3. 详细代码四,实验现象:总结前言 我们继续讲解 stm32 f103,这篇文章将详细 为大家讲…...
C#专栏目录(长期更新)
文章目录C# 基础C#进阶C#应用WPF基础WPF 3D小游戏C# 基础 1996年,微软用年薪三百万美刀的价格从Borland挖来了大神海尔斯伯格,开始了J开发,用以对抗Java。但SUN公司认为此举违反了Java开发平台的中立性,对微软提出诉讼。C#正是在…...
BurpSuite配置抓取HTTPS数据包
简介 我们在渗透测试的过程中,经常会遇到HTTPS的网站,Burp默认是没有办法抓取HTTPS的包的,想要让Burp抓取Https的包也很好办,只需要浏览器安装相关的证书即可,接下来将配置过程做一个记录。 前置条件: 1.J…...
图片转base64格式返回给前端,前端如何展示?
图片以base64形式在页面上展示出来在这里要说到Data URI scheme,它可以直接将一些小的数据直接嵌入到网页中,不需要再引入。支持格式如下data:, 文本数据data:text/plain, 文本数据data:text/html, HTML代码data:text/html;base64, base64编码的HTML代码…...
C++入门知识【超详解】
目录1.认识Chello worldC关键字2.命名空间3.std标准库4.输入输出5.缺省参数6.函数重载7.引用7.1引用的概念7.2引用的场景1.作参数2.作返回值7.3引用的注意点7.4指针和引用的区别8.auto关键字9.基于范围的for循环10.内联函数10.1概念10.2特征11. C98中的指针空值1.认识C hello …...
零基础、非计算机系学Python该如何上手?
首先我觉得要放平心态,不用过多去纠结是不是专业出身这回事。 想学那就认真去学,我们最终目标是掌握Python这门技能。 非计算机专业同时零基础,想自学Python该如何上手?分享我自学Python的几点建议吧。 1、重视基础 Python是一…...
关于 vue3 模板引用
文章目录前言1.访问模板引用2.v-for中的模板引用3.组件上的ref前言 如果我们需要直接访问组件中的底层DOM元素,可使用vue提供特殊的ref属性来访问 1.访问模板引用 在视图元素中采用ref属性来设置需要访问的DOM元素 a. 该ref属性可采用字符值的执行设置 b. 该ref属…...
Redis | 安装Redis和启动Redis服务
目录 一、Redis简介 1.1 简介 二、Redis安装 2.1 Windows安装Redis 2.2 Linux安装Redis 三、Redis服务启动和停止 3.1 Windows启动Redis服务 3.2 Linux启动Redis服务 四、Redis设置密码远程连接 4.1 为Redis登陆设置密码 4.2 设置Redis允许远程连接 五、Redis常…...
博客要考虑的最佳WordPress主题
有太多的选择会瘫痪你做决定的能力。有太多的WordPress主题,但仅仅只需要一个并且它是要合适的。我们建立了数十个 WordPress 博客并安装了数百个主题。根据我们所有的经验,我们发现Newspaper是大多数用户的最佳WordPress博客主题。这个自适应、强大的主…...
C 学习笔记 —— 函数指针
函数指针 上面的第二个char (* f) (int);写法就是函数指针的声明; 首先,什么是函数指针?假设有一个指向 int类型变量的指针,该指针储存着这个int类型变量储存在内存位置的地址。 同样,函数也有地址,因为函…...
FastDDS-3. DDS层
3. DDS层 eProsima Fast DDS公开了两个不同的API,以在不同级别与通信服务交互。主要API是数据分发服务(DDS)数据中心发布订阅(DCPS)平台独立模型(PIM)API,简称DDS DCPS PIM…...
9.2 IGMPv2
实验目的 (1) 熟悉IGMPv2的应用场景 (2) 掌握IGMPv2的配置方法 实验拓扑 实验拓扑如图9-17所示: 图9-17:IGMPv2 实验步骤 配置IP地址(请参考上一个实验)运行IGPÿ…...
巨头混战,抢着“兜底”自动驾驶安全
诚然,中国汽车行业的发展绝对不会拘泥于电动化,必定会在电动化的基础上,迎接下半场的快速智能化。 2021年6月,长城汽车线控底盘全球首次发布。 彼时,长城汽车技术副总裁宋东先宣布,整合了线控转向、线控制…...
RightCapital 第一轮面试题
现在我们就马上开始吧! 答案在文末 JavaScript 是一门单线程的静态类型语言(单选题) 正确 错误 在 JavaScript 中下面哪种类型的值是不可变的(immutable)(单选题) Object Symbol Array Date …...
Python曲线肘部点检测-膝部点自动检测
文章目录一. 术语解释二. 拐点检测肘部法则是经常使用的法则。很多时候,可以凭人工经验去找最优拐点,但有时需要自动寻找拐点。最近解决了一下这个问题,希望对各位有用。一. 术语解释 **肘形曲线(elbow curve)**类似人胳膊状的曲线ÿ…...
【算法题】最大矩形面积,单调栈解法
力扣:84. 柱状图中最大的矩形 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 题意很简单,翻译一下就是:求该图中…...
活动策划|深度分析年货节活动该如何策划!
四月初,不平凡的初春开始恢复往日的平静。对于新零售行业,疫情的缓解也逐渐平稳生态链的运转。2020年新零售的格局在洗礼后,业务的聚焦点也从前端促销转移到后端履约的体验闭环,同时很大程度的推进企业在危机公关下的应对。618大促…...
Idea启动遇到 Web server failed to start. Port 8080 was already in use. 报错
Idea启动遇到问题-记录 报错英文提示: APPLICATION FAILED TO START Description: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to liste…...
Python3中zip()函数知识点总结
1.引言 在本文中,我将带领大家深入了解Python中的zip()函数,使用它可以提升大家的工作效率。 闲话少说,我们直接开始吧! 2. 基础知识 首先,我们来介绍一些基础知识点: Python中的某些数据类型是不可变的…...
过滤器,监听器,拦截器的原理与在Servlet和Spring的应用
在Java Web的开发中,最原始和初期的学习都是从Servlet开始的,Servlet是Java最为耀眼的技术,也是Java EE的技术变革。目前大火主流的框架spring boot也的spring mvc部分也是基于拓展servlet完成的。回到之前的文章spring 实现了对servlet的封装…...
minio spring boot 秒传、分片上传、断点续传文件实现
此处后端使用的是前期封装的自定义starter,具体链接可参考:minio对象存储spring boot starter封装组件 这里主要针对前期封装的组件,做一个简单的应用,前端直传可查看之前的文章 秒传 秒传的逻辑比较简单,在前传上传…...
MTK平台使用Omnipeek分析空口协议讲解
讲解这个之前,我们先来了解下beacon/robe Request/Probe Response 三种帧 beacon帧 信标帧,由AP以一定的时间间隔周期性发出,以此来告诉外界自己无线网络的存在。 Beacon帧作为802.11中一个周期性的帧,Beacon周期调高,对应睡眠周期拉长,故节能(即越来休息100ms再起来…...
string和自动推断类型
欢迎来观看温柔了岁月.c的博客目前设有C学习专栏C语言项目专栏数据结构与算法专栏目前主要更新C学习专栏,C语言项目专栏不定时更新待C专栏完毕,会陆续更新C项目专栏和数据结构与算法专栏一周主要三更,星期三,星期五,星…...
【软件测试】从功能到自动化测试,测试人的进阶之路细节,这些必不可少......
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 测试流程࿰…...
C语言青蛙跳台阶【图文详解】
青蛙跳台阶前言1. 题目介绍2. 解题思路3. 利用图片来演示青蛙跳台阶的原理4. 如何用C语言实现青蛙跳台阶前言 在本文,我们要与一只活泼可爱的小青蛙合作,带领着它跳上台阶,这个小家伙精力充沛,特别擅长于跳跃。我们要让它做我们的…...
笔记(五)——list容器的基础理论知识
list容器是一个双向链表容器,可以高效地进行插入删除元素,但是不能随机存取元素(不支持at()和[]操作符)。一、list容器的对象构造方法list对象采用模板类的默认构造形式例如list<T> lst;#include<iostream>…...
网站体验分析/百度推广seo优化
前言 “E”表示指数间距(Exponential Spacing)。 电阻的标称阻值有6个系列: 序号 系列 误差值1E620%2E1210%3E245%4E482%5E961%6E1920.5%参考文档:https://wenku.baidu.com/view/835a600ad0d233d4b04e6954.html GB文件…...
建设网站群的好处/aso平台
Javascript语言的执行环境是”单线程”(single thread)。 所谓”单线程”,就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。 这种模式…...
青岛网站建设制作/seo查询工具
之前在用Django搭web后端的时候,需要做个小功能就是:用户登陆之后长时间未操作会超时自动退出。 并且当用户做某些事情,比如下载文件,留言,发送email 都需要先检验当前用户还是否处于已登陆状态。 要是在每次事件前面都…...
网站地图怎么做的/建站之星官方网站
给空格子上下左右的互换操作,问最后是怎样的 注意一行的最后一个若是空格,需要自己加注意读取时 操作可能分好多行,一定要读取到 0 为止 1 #include <iostream>2 #include <cstring>3 using namespace std;4 char map[50][50],o…...
深圳福田高端网站建设/中国新闻今日头条
LAMP搭建和配置 LAMP是由Linux, Apache, MySQL, PHP组成的,即把Apache、MySQL以及PHP安装在Linux系统上,组成一个环境来运行PHP的脚本语言。Apache是最常用的Web服务软件,而MySQL是比较小型的数据库软件。…...
工商注册公司名称核准/成都爱站网seo站长查询工具
CyclicBarrier 意思是:环形栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后&…...