Android 子线程为什么不能更新UI?

Android 应用的 UI 是在主线程上进行绘制和更新的。

当我们在子线程中直接进行 UI 更新时,会导致以下问题:

1. 线程安全问题:多个线程同时操作 UI,可能导致 UI 组件的状态不一致或者出现竞争条件。
2. 卡顿和 ANR:如果在主线程中执行耗时操作,会导致主线程被阻塞,用户界面无法响应用户的输入,甚至可能发生 ANR(Application Not Responding)错误。

 移步:子线程怎么切换主线程? 

(一)现象
在子线程中直接更新UI就会crash,报错如下:
android.view.ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.
只有创建了视图层次结构的原始线程才能访问它的视图。
也就是说,ui在哪个线程创建的,就应该在哪个线程中更新。而UI是在主线程中创建的,所以就应该在主线程中更新UI。这是crash的信息提示。

(二)原因
首先要明白UI更新的原理,就是通过不断的测量、布局和绘制的过程。最终都会请求view的绘制操作,即requestLayout()这个方法。

View.java:
public void requestLayout() {
//当父视图不为空且有请求布局时,执行父视图的requestLayout()方法
if (mParent!=null && !mParent.isLayoutRequested()){
mParent.requestLayout();
}
}
protected ViewParent mParent; //当前视图的父视图
//父视图的赋值方法
void assignParent(ViewParent parent) {
if (mParentnull) {
mParent = parent;
} else if (parentnull) {
mParent = null;
} else {
throw new RuntimeException(“view " + this + " being added, but it already has a parent”);
}
}


从源码可以看出,当请求布局时,会调用父视图mParent的requestLayout()方法,mParent是一个接口对象,requestLayout()方法是接口方法。所以需要找到实现这个接口的类。
可以发现,在assignParent(ViewParent parent)方法中对mParent进行了赋值,那么assignParent(ViewParent parent)在哪里被调用的呢?
通过阅读Activity的启动过程源码,可以发现:

(1)在Activity的onCreate()方法中调用了setContentView()方法,其实最终调用了顶层视图PhoneWindow的setContentView()方法,然后调用其内部的installDecor()初始化mDecor;
(2)紧跟着在handleLaunchActivity()中调用了handleResumeActivity();
(3)然后在handleResumeActivity()中可以发现调用了WindowManagerImpl的addView方法,wm.addView(decor, l)
(4)然后又调用了WindowManagerGlobal类的addView()方法,在里面可以发现又调用了root.setView(view, wparams, panelParentView),这个root是ViewRootImpl一个实例。
然后直接看ViewRootImpl的setView()方法:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
//…
view.assignParent(this);
//…
}
}
}

由此可见,最终在ViewRootImpl的setView()方法中对mParent进行了赋值。同时也指明了mParent是ViewRootImpl的一个实例,实现了ViewParent接口,所以很显然了,前面提到的调用了mParent 的requestLayout()方法,即是ViewRootImpl的requestLayout()。

ViewRootImpl.java:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) { // 1.请求更新布局
checkThread(); // 2.首先检查当前所在线程
//…
}
}
void checkThread() {
// 3.直接判断view所属的线程是否为当前线程,否则抛出异常
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadExcept ion( “Only the original thread that created a view hierarchy can touch its views.”);
}
}

final Thread mThread; //存放view所属的线程
所以,在进行UI更新时,都会进行线程的检测判断。以上就是为什么不能在子线程中直接更新UI的原因原理。
为了避免上述问题,Android 引入了一种机制,允许我们在子线程中将任务切换到主线程执行。

移步:子线程怎么切换主线程?
 

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

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

相关文章

Redis有没有可能丢数据

Redis在某些情况下有可能会丢失数据。尽管Redis是一个高性能的内存数据库,但是由于其工作方式和特性,存在一些情况下可能导致数据丢失的情况,包括: 内存溢出: 如果Redis实例的内存用尽,而没有足够的空间来处…

数据结构入门篇 之 【双链表】的实现讲解(附完整实现代码及顺序表与线性表的优缺点对比)

一日读书一日功,一日不读十日空 书中自有颜如玉,书中自有黄金屋 一、双链表 1、双链表的结构 2、双链表的实现 1)、双向链表中节点的结构定义 2)、初始化函数 LTInit 3)、尾插函数 LTPushBack 4)、头…

单据分页的实现

单据分页的实现 1. AceWzcgfkjtMaintainProxy.java package nc.ui.jych.wzcgfkjt.ace.serviceproxy;import nc.bs.framework.common.NCLocator; import nc.itf.jych.IWzcgfkjtMaintain; import nc.ui.uif2.components.pagination.IPaginationQueryService; import nc.vo.jych.…

软考高级:信息系统开发方法2(形式化方法、统计过程方法等)概念和例题

作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

RTC协议与算法基础 - RTP/RTCP

首先,需要说明下,webrtc的核心音视频传输是通过RTP/RTCP协议实现的,源码位于src/modules/rtp_rtcp目录下: 下面让我们对相关的内容基础进行简要分析与说明: 一、TCP与UDP协议 1.1、TCP协议 TCP为了实现数据传输的可…

【Python】新手入门学习:详细介绍依赖倒置原则(DIP)及其作用、代码示例

【Python】新手入门学习:详细介绍依赖倒置原则(DIP)及其作用、代码示例 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、Py…

如何将.txtpb在IDE中彩色高亮显示

1. 问题描述 文件内容片段如下,它采用了一种键值对的格式,其中还包括了注释。我们可以采用一种近似的语言色彩识别方案处理它,比如YAML或者Python的语法高亮规则,因为这两种语言在处理键值对和注释的表示上与内容片段相似。当然也…

【QT+QGIS跨平台编译】之七十三:【QGIS_Analysis跨平台编译】—【错误处理:字符串错误】

文章目录 一、字符串错误二、处理方法三、涉及到的文件一、字符串错误 常量中有换行符错误:(也有const char * 到 LPCWSTR 转换的错误) 二、处理方法 需要把对应的文档用记事本打开,另存为 “带有BOM的UTF-8” 三、涉及到的文件 涉及到的文件有: src\analysis\processin…

spring boot-操作excel(EasyExcel 快速开始)/ spring boot接受文件参数 File

文章目录 一、spring boot 操作excel1. 技术选型1.1 EasyExcel1.2 POI 二、EasyExcel使用0. 工作中使用总结1. maven 引入2. demo1:excel写入文件3. demo2:SpringBoot项目中集成EasyExcel实现Excel文件的下载response的三个属性:编码、类型、…

gcc -static参数

在使用 GCC(GNU Compiler Collection)编译器编译C语言或C语言程序时,-static 选项告诉编译器生成一个完全静态链接的可执行文件。这就意味着程序需要的所有库在编译时都会被包含在执行文件中,它不会在运行时链接动态库&#xff08…

openssl3.2 - exp - 选择最好的内建椭圆曲线

文章目录 openssl3.2 - exp - 选择最好的内建椭圆曲线概述笔记将 openssl ecparam -list_curves 实现迁移到自己的demo工程备注END openssl3.2 - exp - 选择最好的内建椭圆曲线 概述 在openssl中使用椭圆曲线, 只允许选择椭圆曲线的名字, 无法给定椭圆曲线的位数. 估计每种椭…

储能系统--户用储能市场现状(三)

1、户用储能市场现状 2022年,俄乌冲突造成能源价格飙升,欧洲居民电价飞涨,成为点燃户储需求的引线。以德国为例,2022年的居民电价达到40欧分/kWh以上,相比2021年初翻了三倍。因此2022年被称为户储爆发元年&#xff0c…

深度学习armv8/armv9 cache的原理

文章目录 前言1、为什么要用cache?2、背景:架构的变化?2、cache的层级关系 ––big.LITTLE架构(A53为例)3、cache的层级关系 –-- DynamIQ架构(A76为例)4、DSU / L3 cache5、L1/L2/L3 cache都是多大呢6、cache相关的术语介绍7、cache的分配策略(alocat…

Llama-3即将发布:Meta公布其庞大的AI算力集群

Meta,这家全球科技巨头,再次以其在人工智能(AI)领域的雄心壮志震惊了世界。3月13日,公司在其官方网站上宣布了两个全新的24K H100 GPU集群,这些集群专为训练其大型模型Llama-3而设计,总计拥有高…

C++函数 加括号与不加括号

很多时候,我们会看到一些在创建对象时有的加括号有的不加括号 那么,这是什么情况呢? 总结:函数需要加上括号,加上括号会对函数初始化,不加括号可能导致未知错误 我们来验证一下。 1.基本数据类型不带括…

利用Python进行网络爬虫:Beautiful Soup和Requests的应用【第131篇—Beautiful Soup】

利用Python进行网络爬虫:Beautiful Soup和Requests的应用 在网络数据变得日益丰富和重要的今天,网络爬虫成为了获取和分析数据的重要工具之一。Python作为一种强大而灵活的编程语言,在网络爬虫领域也拥有广泛的应用。本文将介绍如何使用Pyth…

C++进阶学习

模板编程 模板函数和模板类的基本概念和用法 模板编程是C中一种强大的特性,它允许程序员编写与类型无关的代码。这意味着你可以编写一个函数或类,让它能够处理任何数据类型。这不仅可以提高代码的重用性,还可以提高编程效率和程序的可维护性…

Verilog——Verilog的历史

第1节 Verilog的历史 在传统硬件电路的设计方法中,当设计工程师需要设计一个新的硬件、数字电路或数字逻辑系统 时,需要为此设计并画出一张线路图,随后在CAE(计算机辅助工程分析)工作站上进行设计。所 设计的线路图由线…

.Net Core 与数据库

查询 Linq var indexList new long[] { 1, 2, 3}; List<long> list new List<long>(); if (String.IsNullOrWhiteSpace(request.Key) false) {var ret from aa in _db.TblAAjoin bb in _db.TblBBon aa.PId equals bb.Idjoin cc in _db.TblCCon aa.CId equals…

13、Linux-Shell02:参数传递和运算符

目录 一、参数传递 二、运算符 1、算术运算符&#xff08;、-、*、/、%、、、&#xff01;&#xff09; 2、关系运算符 3、逻辑运算符 4、字符串运算符 5、文件运算符 一、参数传递 执行脚本时可以为脚本文件传递参数&#xff0c;在脚本中可以处理这些参数。 第n个参数…