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 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 题意很简单,翻译一下就是:求该图中…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
