Android UI绘制原理:UI的绘制流程是怎么样呢?为什么子线程不能刷新UI呢?讲解大体的流程是怎么样的

news2024/12/23 4:48:13

目录:

  1. 为什么子线程不能刷新UI呢,原因是什么?
  2. UI绘制原理
    2.1 创建Activity 实例和view的树型结构
    2.2 管理绘制的类:ViewRootImpl
    2.3 是如何触发刷新View的?
    2.4 View的绘制流程:测量(Measure)
    2.5 View的绘制流程:布局(Layout)
    2.6 View的绘制流程:绘制(Draw)
  3. 学习总结

在这里插入图片描述


一、 为什么要学习android UI绘制原理呢?对我们有什么帮助?

1.解决复杂布局问题:了解UI绘制原理可以帮助我们更好地理解和解决布局问题,比如使用自定义View、优化布局层级等。

2.知道何时触发布局(Layout)、绘制(Draw)和测量(Measure)过程,以及如何减少这些过程的调用次数,避免在UI线程上进行耗时的操作,可以显著提升应用的流畅度和响应速度。



二、为什么子线程不能刷新UI呢?原因是什么?


比如,我们写如下这样的代码,那么就有可能报错。

 Thread(object :Runnable{
            override fun run() {
                tvTestUi.text = "123412341234"
            }
        }).start()

报错内容:只有在主线程种对UI进行操作才行。

Only the original thread that created a view hierarchy can touch its views.

我们可以追踪到源码里面看看。后面我们会讲一下原因。
下面我们可以看到,绘制UI的线程,如果不是主线程,那么就报错。
在这里插入图片描述但,为什么不能子线程呢?多线程更新UI不是会更加高效? 只是因为代码里面限制?!!当然不是。

子线程不能直接刷新UI的原因主要与Android系统的UI线程(主线程)的设计和机制有关。在Android中,UI组件(如视图、控件等)不是线程安全的,这意味着它们的设计初衷是为了在单个线程(即UI线程或主线程)上被访问和修改。如果多个线程尝试同时修改UI组件,就可能会导致不可预见的行为,比如视图的不一致状态、崩溃等。

假如你可以多线程更新,那么你得花时间确定更新状态是否一致,界面重复刷新问题,像素结果是否统一的问题,要同步,所以代价是相当大的,所以绝大多数的系统,对UI刷新,都是采用单线程的方式

但,为什么在oncreate中开子线程刷新ui不会报错呢?看源码我们就会知道,viewRootImpl 的初始化在 onCreate 之后,onResume 之后。所以也就没调用checkThread方法。

具体来说,当Activity调用setContentView()时,它会通过WindowManager(实际上是WindowManagerGlobal和WindowManagerImpl)来请求添加一个窗口(Window)。这个过程中,会创建并初始化ViewRootImpl实例,然后将其与Activity的根视图(DecorView)关联起来。

由于ViewRootImpl的初始化是异步的,并且涉及到与窗口系统的交互,因此很难直接通过Activity的生命周期方法来准确判断ViewRootImpl的初始化完成时刻。但是,我们可以知道,在onResume()之后,并且视图开始绘制之前,ViewRootImpl应该已经被初始化了


三、UI绘制原理


我们再回到上面的代码。

Thread current = Thread.currentThread(); 这行代码的意思是获取当前正在执行的线程对象,那么当前运行的线程是什么线程??为什么会调用ViewRootImpl的checkThread方法呢??为什么text的时候,会重新绘制呢?

这,就需要我们了解UI的绘制流程

Android UI的绘制流程是一个从数据加载到Activity启动,再到View的测量、布局和绘制的过程。我们直接从创建Activity实例这里开始。

在这里插入图片描述下面我们讲一下流程。从创建Activity开始。


3.1 创建Activity 实例和view的树型结构

ActivityThread,通过handleResumeActivity方法创建Activity实例后,并为其创建一个PhoneWindow,合成DecorView。

在这里插入图片描述
在这里插入图片描述我们可以看到当我们调用setContentView的时候,就是调用了window的。
在这里插入图片描述

DecorView是顶级容它内部包含了一个或多个子View或ViewGroup,用于承载应用的UI内容。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述那么接下来,view创建好后,如果我想要进行view的渲染和刷新?由谁来做呢?是如何触发刷新的?下面我们看看ViewRootImpl




3.2 是如何触发刷新view的?

我们先了解一下,VSYNC是什么?VSYNC信号由屏幕(显示设备)产生,并以固定频率发送给Android系统。Android系统中的SurfaceFlinger接收VSYNC信号后,会遍历其层列表以查找新的缓冲区进行渲染。这种机制提升了渲染任务的优先级,优化了渲染性能。

刷新也分为手动刷新和自动刷新(VSYNC就是自动刷新),比如我们调用textView的text方法,就是手动刷新,会调用requestLayout方法,不断的递归requestLayout方法去进行刷新。因为它是一个树形结构。

自动刷新,其实就是一个回调。下面是源码,可以粗略看看。
在这里插入图片描述在这里插入图片描述
performTraversals();方法就是会绘制的方法,比如测量等
在这里插入图片描述


3.3 管理绘制的类:ViewRootImpl

我们都知道,写的这些xml布局代码,都是一个树形的层次结构,比如下面的代码,就对应一个这样的结构(如图),举例哈:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.main.MainActivity">


    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/home_fragmentcontainerview"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/home_nav" />

    <TextView
        android:id="@+id/tv_test_ui"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

在这里插入图片描述而这些结构呢,是由ViewRootImpl来进行管理,比如进行测量,布局以及绘制等等。

View的绘制流程是通过ViewRootImpl类来进行管理,在ActivityThread(主线程)中,当Activity对象被创建完毕后,会将DecorView添加到Window中,并创建对应的ViewRootImpl对象,将两者建立关联。

我们可以看到是ActivityThread线程创建了ViewRootImpl,所以Thread current = Thread.currentThread();获得的当前线程,就是主线程。如果是子线程刷新,那么Thread current = Thread.currentThread()就是子线程。


2.4 View的绘制流程:测量(Measure)

为什么需要测量呢?确定View的宽高,用于后续绘制。

有没有想过,wrap_content和match_parcent的宽高如何确定呀,就需要测量,并且每个view的宽高,还要取自于上一层的,所以ViewGroup遍历所有子View进行测量,根据子View的LayoutParams和自身的MeasureSpec计算出子View的测量规格。


2.5 View的绘制流程:布局(Layout)

根据测量的宽高确定View在其父View中的位置(即四个顶点的坐标)。也是会递归遍历对子View进行布局。


2.6 View的绘制流程:绘制(Draw)

这个阶段的作用,就是将View的内容绘制到屏幕上。也是会递归遍历子View,调用子View的draw方法。

到这里了,view的绘制流程就大致完成了。


三、学习总结


刚开始看UI绘制原理的时候,完全看不懂,硬着头皮去看,渐渐的有些可以看懂了,但绝大部分还是不懂。这个时候,我就从“为什么子线程不能刷新UI呢?”入手,比如不能刷新原因是什么,了解原因后,你懂了,但你会发现你不懂的地方也会更多,但是,你已经知道你有哪些不懂了,这个时候,你重新回头去看第二篇的时候,你思路就清晰很多了,你又能看懂很多了。

所以,第一次看肯定有很多不懂,那么就第二次,第三次。慢慢的你就有思路,开始知道一些东西,熟能生巧,很多人都是看一次,觉得难就不学了,但很多东西,都是需要经历无数次,你才会熟悉,才会熟练。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2080862.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1998-2023年上市公司研发投入数据

1998-2023年上市公司研发投入数据 1、时间&#xff1a;1998-2023年 2、来源&#xff1a;上市公司年报 3、指标&#xff1a;证券代码、统计截止日期、数据来源、报表类型、研发人员数量&#xff08;人&#xff09;、研发人员数量占比(%)、研发投入金额&#xff08;元&#xf…

MyBatis一级缓存和二级缓存以及 mybatis架构

缓存 数据缓存&#xff0c;让数据离我们执行的程序更近一点&#xff0c;让程序能够快速的获取到数据 缓存的作用就是减轻数据库的压力&#xff0c;提高查询性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁&#xff0c;而是存储在内内&#xff08;缓存&…

亚马逊无货源店群模式是什么?2024年还能做吗?

相信众多的电商卖家&#xff0c;对店群模式并不陌生。近年来国内电商的店群模式竞争激烈&#xff0c;近乎饱和。那么&#xff0c;在国际知名的跨境电商平台亚马逊上&#xff0c;店群模式又是如何运作呢&#xff1f;到了2024年&#xff0c;亚马逊的店群模式还值得做吗&#xff1…

EAST文本检测

原文:EAST文本检测 - 知乎 (zhihu.com) 一、文本检测 论文: https://arxiv.org/pdf/1704.03155.pdf​arxiv.org/pdf/1704.03155.pdf 一般的文本检测模型会分多个阶段(multi-stage)进行,在训练时需要把文本检测切割成多个阶段(stage)来进行学习,这种把完整文本行先分…

表达式求值问题的实现

这是C算法基础-数据结构专栏的第二十二篇文章&#xff0c;专栏详情请见此处。 引入 相信大家都会做如1(23)*4/5-6这样的算式&#xff0c;而我们今天要学习让计算机通过表达式求它的值&#xff0c;这就是表达式求值问题。 下面我们就来讲表达式求值问题的实现。 定义 表达式求值…

mysql的聚簇索引、非聚簇索引、回表

1.聚簇索引和非聚簇索引 聚簇索引&#xff08;聚集索引&#xff09;&#xff1a;数据和索引放在一起&#xff0c;B树的叶子节点存放了整行数据&#xff0c;有且只有一个。 【主键索引和唯一索引&#xff0c;主键唯一&#xff0c;存放的是主键对应的整行数据】非聚簇索引&#…

[CR]厚云填补_Diffusion Enhancement for CR

Diffusion Enhancement for Cloud Removal in Ultra-Resolution Remote Sensing Imagery Abstract 云层的存在严重影响了光学遥感图像的质量和有效性。然而&#xff0c;现有的基于深度学习(DL)的云移除(CR)技术&#xff0c;通常以保真度驱动的损失作为约束&#xff0c;例如L1或…

USB3.2 摘录(11)

系列文章目录 USB3.2 摘录&#xff08;一&#xff09; USB3.2 摘录&#xff08;二&#xff09; USB3.2 摘录&#xff08;三&#xff09; USB3.2 摘录&#xff08;四&#xff09; USB3.2 摘录&#xff08;五&#xff09; USB3.2 摘录&#xff08;六&#xff09; USB3.2 摘录&…

OpenAI神秘“草莓”项目 计划最早今年秋季推出

据科技媒体The Information报道&#xff0c;OpenAI神秘“草莓”项目&#xff0c;计划最早今年秋季推出&#xff01;上个月&#xff0c;OpenAI的内部团队被曝出正开发的“草莓”&#xff08;Strawberry&#xff09;项目&#xff0c;目的是增强OpenAI的模型的推理能力&#xff0c…

大模型从入门到精通—— LLM 应用评估(一)

如何评估 LLM 应用 1. 简介 在使用大型语言模型&#xff08;LLM&#xff09;构建应用程序时&#xff0c;评估的思路往往与传统的 AI 开发有所不同。传统 AI 的评估可能依赖于大规模的标注数据集和精细的性能指标&#xff0c;而 LLM 的开发则更注重迭代验证&#xff0c;并在开发…

python从入门到精通:异常操作、模块操作及包操作

目录 1、异常概念 2、异常的捕获方法 3、异常的传递 4、python模块 4.1、模块的导入 4.2、自定义模块 5、python包 5.1、自定义python包 5.2、安装第三方包 1、异常概念 当检测到一个错误时&#xff0c;python解释器会无法执行&#xff0c;反而出现一些错误的提示&a…

视频中间件:大华视频设备接入管理应用

前言 上篇博文介绍了视频中间件&#xff1a;海康视频设备的接入管理&#xff1f;&#xff0c;今天给大家带来大华视频设备的接入管理&#xff0c;视频中间件平台支持大华Sdk、大华主动注册、Onvif、Rtsp、Gb28181等方式对大华视频设备的接入管理。同时视频中间件可支持协议互转…

离子交换技术在含银废水处理中的创新应用

随着科技的不断进步&#xff0c;金属制品在各行各业中扮演着越来越重要的角色。其中&#xff0c;镀银工艺作为一种提高金属制品性能的重要手段&#xff0c;其环境影响也日益受到关注。镀银过程中产生的含银废水含有多种有害物质&#xff0c;对环境构成潜在风险。因此&#xff0…

百度:未来or现在 顾此失彼?

用AI押注未来&#xff0c;却丢了现在 国内AI先行者百度 走到哪了&#xff1f; 作为这个星球最热门的概念&#xff0c;AI无疑是个好故事&#xff0c;不只是百度&#xff0c;美股的一众科技公司几乎都在讲述自己的AI投入及发展成果&#xff0c;市值也随着AI预期坐过山车。而市场…

Datawhale AI夏令营第五期CV方向-城市管理违规行为智能识别-Task1

赛题解析 城市管理违规行为智能识别 初赛任务是根据给定的城管视频监控数据集&#xff0c;进行城市违规行为的检测。违规行为主要包括垃圾桶满溢、机动车违停、非机动车违停等。 选手需要能够从视频中分析并标记出违规行为&#xff0c;提供违规行为发生的时间和位置信息。 数…

ArkTS--状态管理

一、概述 在声明式UI编程范式中&#xff0c;UI是应用程序状态的函数&#xff0c;应用程序状态的修改会更新相应的UI界面。ArkUI采用了MVVM模式&#xff0c;其中ViewModel将数据与视图绑定在一起&#xff0c;更新数据的时候直接更新视图。如下图所示&#xff1a; ArkUI提供了一系…

Day49 | 53. 寻宝 prim算法 与kruskal算法

语言 Java 53. 寻宝 53. 寻宝&#xff08;第七期模拟笔试&#xff09; 题目 题目描述 在世界的某个区域&#xff0c;有一些分散的神秘岛屿&#xff0c;每个岛屿上都有一种珍稀的资源或者宝藏。国王打算在这些岛屿上建公路&#xff0c;方便运输。 不同岛屿之间&#xff0c…

【MATLAB学习笔记】绘图——自定义标记(Marker)形状,实现与MATLAB自带标记基本一致的功能(自适应缩放、自适应裁剪)

目录 前言自定义标记函数自定义标记函数的说明纵横比调整将图形大小按磅数设置平移标记点绘制标记点边界标记点不裁剪 拓展功能——标记点自适应绘图区的缩放绘图区缩放回调函数标记点大小自适应标记点裁剪自适应 示例基本绘图自定义标记函数的使用 总代码主函数自定义标记函数…

RKNPU2从入门到实践 --- 【4】RKNN 模型构建【使用pycharm一步一步搭建RKNN模型】

目录 前言 1.1 RKNN 初始化及对象释放 1.1.1 概念介绍 1.1.2 实际演示 1.2 RKNN 模型配置 1.2.1 概念介绍 1.2.2 实际演示 1.3 模型加载 1.3.1 概念介绍 1.3.1.1 Caffe模型加载接口 1.3.1.2 TensorFlow模型加载接口 1.3.1.3 TensorFlowLite 模型加载接口 1.3.…

debian12 - 修改SSH端口连接回包

文章目录 debian12 - 修改SSH端口连接回包概述笔记先猜猜回包是哪个程序回的去下载对应版本的openssh代码工程用telnet测试的效果todo 关于ssh状态为active(start)的原因END debian12 - 修改SSH端口连接回包 概述 和同学讨论问题。 他说&#xff0c;用telnet去连接SSH端口&am…