当前位置: 首页 > news >正文

深入浅出Android同步屏障机制

原文链接 Android Sync Barrier机制

诡异的假死问题

前段时间,项目上遇到了一个假死问题,随机出现,无固定复现规律,大量频繁随机操作后,便会出现假死,整个应用无法操作,不会响应事件,会发生各种奇怪的ANR,且trace不固定。非常之诡异。

经过大量的复现研究和分析, 以及大神的指点后,发现与同步屏障(Sync Barrier)有关系,于是发现有必要研究一下这个东西。

什么是Sync Barrier机制

这是安卓线程消息队列里面的一个新增加的东西,这么说还是太抽象,我们从头说起这件事情:

安卓的消息队列机制

消息队列,或者叫做Event Loop,通常在任何一个GUI应用程序里面都会有的,应用大部分时间处于Idle状态,当有事件发生时,比如用户点了一个button,然后开始响应此事件。安卓也是一个GUI应用程序,绝大多数都是带有GUI的应用程序,那么安卓 里面是如何实现这个EventLoop的呢,它是用Looper和MessageQueue,以及Handler,以一种消息队列的方式来实现loop。

有一定经验的同学对这些东西肯定不陌生,因为它们在实际的开发过程中相当常见,比如说对于UI的操作只能放在主线程里面,那么当工作线程想要更新UI时就需要用Handler发一个消息,或者post一个Runnable。或者当你想延后一段时间执行某种操作,就可以用postDelayed。这些都是非常常规的操作了。对于工作线程,如果想启用消息队列,就用Looper#prepare就可以了,当然了,要记得quit。

内部原理上面也不是很复杂,就是Looper会给线程绑定一个消息队列,即是MessageQueue,这是一个无限循环的队列,不断的轮询队列,当有新的消息时就去处理,否则就等待。主线程,安卓框架层在创建应用进程的时候就会给主线程默认创建好MessageQueue,所以就可以向其发消息(sendMessage)或者postDelayed,它们本质上都是一样的,都是向MessageQueue中入队一个消息,稍后它便会得到处理。

在这里插入图片描述

同步消息与异步消息

这个MessageQueue机制,就是队列,也就是说符合队列的特点,先进先出(FIFO,First-In First Out),就是说你先post的消息,肯定是先被处理,后post的后处理,即使有delay时候,也是看谁先到,谁先到谁先被处理。因此,这里面的消息全是同步,也就是说所有消息都是顺序处理,这就是同步消息。

异步消息,也就是说某个消息,想被最高优先级处理,无视发送消息的时机,比如说队列里面有8个消息,如何想让某个消息最先被处理?这时队列就变成了优先队列,有优先级的队列。那么具有高优先级的消息也是异步消息(Asynchronous Message)。即使是最后加入队列的,但因为是异步消息,它会被先处理,并不是FIFO,此可理解 为异步。

Sync Barrier用以实现优先队列

说了这么多,Sync Barrier就是安卓 内部用以实现优先级队列的一种方式。

当队列中出现Sync barrier(具体实现上就是Message#target为null)时,就会忽略所有同步消息,寻找异步消息(isAsynchrouns为true)的消息,然后优先处理它。

需要注意的是,把消息标记为异步,以及向消息队列中发送Sync barrier,这些API全部都是hide的,也就是说app中是无法使用的,通过反射也许能调用成功,但风险也较大,后续会被谷歌限制调用。换言之,这东西只能在Frameworks层内部自己使用。

为什么要有Sync Barrier

说了这么多,其实本质上,这东西就是一个优先队列,给要处理的消息加一个优先级机制,那这有什么实际用途呢?

消息队列这东西是在安卓一诞生就有了的东西,大部分时候它也没有什么问题。但有一个事情,就是安卓操作系统的UI流畅度远不及水果平台(iOS),原因就是在于水果平台的UI渲染是整个系统中最高优先执行。

有同学会说安卓里面也是这样啊,你想UI都只能在主线程里面操作(因此主线程也叫UI线程)。只能在主线程中操作UI,就能保证UI渲染是最高优先级吗?当然不是了。因为整个应用程序的默认线程就是主线程,换句话说,如果你不明显的去做线程切换,或者启用工作线程,那么所有事情都发生在主线程里面,当然 也包括了UI渲染,因此UI的渲染与你在主线程时面post一个消息的优先级是一样的。

如何让UI渲染在主线程中以最高优先级运行?于是就有了Sync barrier机制,这东西就是为了让消息队列有优先级,并且没有开放给app使用。可以去看一下ViewRootImpl(这货是专门负责ViewTree渲染的,也即可以理解为负责UI渲染的)的几个perform,它都是异步消息,也即会开启Sync barrier,它发送的消息将会是最高优先级的,会被优先处理。

主要在哪里用Sync barrier

前面提到了,Sync barrier这玩意儿并不是给app开发同学用的,很多相关的接口并没有开放出来,这是为了提高UI渲染而设计的东西。因此这东西主要是用在了UI渲染过程中。

仔细查看ViewRootImpl的源码可以发现,每次渲染View tree之前都会先给主线程插入一个Sync barrier,以挡住同步消息,以保证渲染被主线程优先执行到。

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}}void unscheduleTraversals() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);}}void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);performTraversals();}}

这里的逻辑略复杂一些,View tree本身的处理过程,也即三大步measure, layout和draw,也就是performTraversal本身并没有异步消息,它是在准备渲染的时候放一个sync barrier,而在具体处理每一帧前就移除了sync barrier,这里为何要这样,还没有完全想清楚。通过搜索ViewRootImpl可以发现只有input event,keyevent 以及与用户输入相关的消息被设置为了asynchronous,也就是说用户事件响应被提高了优先级,而view tree的渲染,即UI的每一帧,其实并没有被提升优先级。因为UI刷的每一帧是以固定频率刷新的,Choreographer 从硬件得到vsync脉冲信号,然后回调给ViewRootImpl让其渲染每一帧(也即是performTraversal)。

Sync Barrier会引发什么问题

说实话,这套机制,实现的并不怎么优雅,因为,毕竟它并不是在最初的设计之初就考虑到的东西,它的整体运行机制并不完善,非常依赖于调用者的使用,所以它的相关API并未有开放出来。

它有三步,先发一个Sync barrier,然后发送异步消息,然后再移除Sync barrier。

只有UI渲染(ViewTree的相关操作,才需要这样做),大部分其他的消息都是同步的,并不需要这样搞。当有Sync barrier时,消息队列在处理消息的时候会忽略掉所有的同步消息(也即是常规消息),优先处理异步消息,直到Sync barrier移除,也是需要手动移除的。Sync barrier需要手动移除是最坑的。

因此,假如要处理的异步特别多,或者逻辑出错Sync barrier没有被移除,那就悲剧 了,就会导致消息队列中的大量常规消息无法得到处理,队列就会停止工作,应用会出现随机的ANR,以及假死。

如何调试

很不幸,Sync barrier导致的问题很难调试,甚至很难被发现,通常都是ANR或者说卡死问题。

那么首先可以按照ANR和卡死的常规分析方式去分析,假如都未发现明显的问题时,比如没有明显的耗时的操作,也没有死锁,也没有被硬件和IO阻塞,也没有进入死循环。

这些常规的分析,都没有发现问题。这时就可以考虑是不是Sync barrier在搞鬼。特别当涉及一些诡异的UI状态时,比如某个View只显示 了一半,比如某一个View没有显示 完全,比如只有背景没有前景,等等,当排除了其他常规问题时,就很可能是Sync barrier有异常导致的。

另外,如果有能力修改Frameworks的话,可以给MessageQueue增加dump信息,把队列中的所有消息都打印出来,以及把Sycn barrier也都打印出来,这样能够比较清楚看到,队列内部的情况,自然也能够发现异常的Sync barrier。

如何避免Sync Barrier搞鬼

前面提到过,这套东西都是Frameworks层内部的机制,并没有开放给app使用,而Frameworks内部的逻辑一般来说还是相当健壮的,绝大多数时候并不会出问题。当然了,各个厂商内部搞的各种所谓优化,倒是有可能会引发问题。

在实际开发过程中,引发Sync barrier的最多场景就是自定义View。对于自定义View,是能够在非主线程调用其invalidate的,当有大量的非主线程调用invalidate时,就有可能恰好与主线程的渲染发生交互,具体case非常corner要刚巧非主线程在postInvalide,然后主线程也刚巧在发送异步消息,就可能使得Sync barrier没有被移除,从而导致问题。

这就需要我们在编码阶段做好封装,对于自定义View的刷新触发逻辑做好封装,做一下线程切换,以保证是在主线程里面执行invalidate。因为暴露出去的接口,是没有办法控制的,你没有办法让所有调用者都在主线程里面调用你的接口。

参考资料

  • Handler sync barrier(同步屏障)
  • Android 同步屏障机制(Sync Barrier)
  • 同步屏障?阻塞唤醒?和我一起重读 Handler 源码
  • 同步屏障与异步消息,从入门到放弃
  • 面试官:如何提高Message的优先级
  • 今日头条 ANR 优化实践系列 - Barrier 导致主线程假死

原创不易,打赏点赞在看收藏分享 总要有一个吧

相关文章:

深入浅出Android同步屏障机制

原文链接 Android Sync Barrier机制 诡异的假死问题 前段时间,项目上遇到了一个假死问题,随机出现,无固定复现规律,大量频繁随机操作后,便会出现假死,整个应用无法操作,不会响应事件&#xff…...

工程管理系统简介 工程管理系统源码 java工程管理系统 工程管理系统功能设计

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展,企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性,公司对内部工程管…...

Python 专栏目录索引

文章目录 Python 环境搭建Python 语法 变量、print、注释和运算符Python 的基本结构Python 中的文件和文件夹操作Python 中常用库Python 常见问题及解决方案Python 应用实例 Python 环境搭建 vscode搭建Python环境 Python 语法 变量、print、注释和运算符 python语法 变量、…...

SSM学习

技术架构 crm的技术架构: 视图层(view):展示数据,跟用户交互。 html, css,js,jquery,bootstrap(ext / easyUI),jsp控制层(Controller):控制业客处理流程(接收请求,接收参数,封装参数;根据不同的请求调用业务 (servlet, ) springMVC ( , webwork,strutsl,struts2)业…...

.net项目部署Docker

1、项目生成的bin目录下创建Dockerfile文件 #运行环境描述,此处是用的Net5构建镜像 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build #复制文件到 docker容器中的app文件夹中 COPY . /app #设置工作目录为 app 文件夹,要和上面一致哦 WORKDIR /app #设…...

Ubuntu无法加载exfat的USB存储设备

当接入设备USB存储设备提示: 不能挂在63GB 卷 Error mounting /dev/sdb1 at /media/ubuntu/83C9-26F4: Command-line mount -t "exfat" -o "uhelperudisks2,nodev,nosuid,uid1000,gid1000,iocharsetutf8,namecase0,errorsremount-ro,umask0077"…...

【计算机网络】网络编程接口 Socket API 解读(2)

Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。 本文讲述的 socket 内容源自 Linux 发行版 centos 9 上的 man 工具,和其他平台(比如 os-x …...

【黄啊码】PHP如何防止重复提交

防抖(Debounce)是一种防止重复提交的策略,它通过延迟一定时间来合并连续的操作,以确保只执行一次。 以下是几种防抖的实现方法以及对应的代码示例: 1. 前端 JavaScript 实现: 在前端使用 JavaScript 实现…...

2594. 修车的最少时间

文章目录 Tag题目来源题目解读解题思路方法一:二分枚举答案 写在最后 Tag 【二分枚举答案】【数组】 题目来源 2594. 修车的最少时间 题目解读 给你一个表示机械工能力的数组 ranks,ranks[i] 表示第 i 位机械工可以在 r a n k s [ i ] ∗ n 2 ranks[…...

vue 使用qrcode生成二维码并可下载保存

安装qrcode npm install qrcode --save代码 <template><div style"display: flex; flex-direction: column; align-items: center; justify-content center;"><div>查看溯源码&#xff0c;<a id"saveLink" style"text-decorati…...

网络融合的发展思路

虽然移动和固定网的融合代表了下一代网络的发展方向&#xff0c;但是目前移动和固定网的 发展还是独立的&#xff0c;有着各自的演进方式&#xff0c;要实现两个网络的完全融合是一个长期 的、逐步发展的过程。 网络融合的体系结构首先应坚持网络分层和功能分离的原则&#…...

报考浙江工业大学MBA项目如何选择合适的辅导班?

浙江工业大学MBA项目每年有数百人报考&#xff0c;在浙江省内除了浙大以外算是人数比较多的一个项目。2023级的招生中第一志愿也通过复试刷掉了百来人&#xff0c;在省内其实作为第一志愿报考的风险在逐渐增大&#xff0c;考生们如果坚持报考&#xff0c;则在针对联考初试的备考…...

算法训练第五十八天

总结&#xff1a;今日事单调栈的开端&#xff0c;还是挺巧妙的。 496. 下一个更大元素 I - 力扣&#xff08;LeetCode&#xff09; 代码&#xff1a; class Solution { public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& …...

如何快速生成一个H5滑动的卡片(单页和分页都有)

单页 <ul class"combo"><li v-for"(item, index) in arr" :key"index"><div class"combo-name">{{ item.A }}</div><div class"combo-price">{{ item.B }}</div><div class"co…...

嵌入式开发笔试面试

C语言部分&#xff1a; 1.gcc的四步编译过程 1.预处理 展开头文件&#xff0c;删除注释、空行等无用内容&#xff0c;替换宏定义。 gcc -E hello.c -o hello.i 2.编译 检查语法错误&#xff0c;如果有错则报错&#xff0c;没有错误则生成汇编文件。 gcc -S hello.i -o h…...

2023国赛数学建模B题思路分析 - 多波束测线问题

# 1 赛题 B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术。声波在均匀介质中作匀 速直线传播&#xff0c; 在不同界面上产生反射&#xff0c; 利用这一原理&#xff0c;从测量船换能器垂直向海底发射声波信 号&#xff0c;并记录从声波发射到…...

thinkphp6 入门(5)-- 模型是什么 怎么用

一、模型 MVC架构 之前开发一个功能&#xff0c;后端为在控制器&#xff08;C&#xff09;中写 php SQL&#xff0c;前端为在页面&#xff08;V&#xff09;中写html css js&#xff0c;这就形成了 VC 架构。 但是发现&#xff0c;相同的数据逻辑&#xff08;SQL&#xf…...

Hadoop HDFS 高阶优化方案

目录 一、短路本地读取&#xff1a;Short Circuit Local Reads 1.1 背景 ​1.2 老版本的设计实现 ​1.3 安全性改进版设计实现 1.4 短路本地读取配置 1.4.1 libhadoop.so 1.4.2 hdfs-site.xml 1.4.3 查看 Datanode 日志 二、HDFS Block 负载平衡器&#xff1a;Balan…...

通俗易懂讲解大模型:Tokenizer

Tokenizer Tokenizer 是 NLP pipeline 的核心组件之一。Tokenizer 的目标是&#xff1a;将文本转换为模型可以处理的数据。模型只能处理数字&#xff0c;因此 Tokenizer 需要将文本输入转换为数字输入。 通常而言有三种类型的 Tokenizer &#xff1a;Word-based Tokenizer、Cha…...

nested exception is java.io.FileNotFoundException

完整的错误信息&#xff1a; [main] ERROR o.s.boot.SpringApplication - Application run failed org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.heima.article.ArticleApplication]; nested exception is java…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...