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 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 题意很简单,翻译一下就是:求该图中…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
