子线程更新主线程的View抛出异常全过程
上篇文章Android子线程真的不能刷新UI吗(一复现异常复现了子线程修改UI的异常。这篇文章详细跟踪setText方法是怎么导致抛出异常的。本文目录子线程更新主线程的View会有什么后果过程具体是怎样的TextView.checkForRelayout的作用是什么View.requestLayout()源码有干了啥ConstraintLayout 有没有重写requestLayout方法ConstraintLayout的父布局是DecorView有没有重写requestLayout方法DecorView的mParent是谁ViewRootImpl.requestLayout又干了啥子线程更新主线程的View会有什么后果会抛异常过程具体是怎样的24698-24800/com.zj.androidthreaddemo E/AndroidRuntime: FATAL EXCEPTION: Thread-3 Process: com.zj.androidthreaddemo, PID: 24698 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8632) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1380) at android.view.View.requestLayout(View.java:23377) at android.view.View.requestLayout(View.java:23377) at android.view.View.requestLayout(View.java:23377) at android.view.View.requestLayout(View.java:23377) at android.view.View.requestLayout(View.java:23377) at android.view.View.requestLayout(View.java:23377) at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3239) at android.view.View.requestLayout(View.java:23377) at android.widget.TextView.checkForRelayout(TextView.java:9221) at android.widget.TextView.setText(TextView.java:5935) at android.widget.TextView.setText(TextView.java:5776) at android.widget.TextView.setText(TextView.java:5733) at com.zj.androidthreaddemo.MainActivity$1.run(MainActivity.java:23) at java.lang.Thread.run(Thread.java:784)TextView.setText()源码publicfinalvoidsetText(CharSequencetext){//调用重载setText(text,mBufferType);}publicvoidsetText(CharSequencetext,BufferTypetype){//再次调用重载setText(text,type,true,0);if(mCharWrapper!null){mCharWrapper.mCharsnull;}}privatevoidsetText(CharSequencetext,BufferTypetype,booleannotifyBefore,intoldlen){if(mLayout!null){//在这里调用了checkForRelayoutcheckForRelayout();}...}TextView.checkForRelayout的作用是什么android 6.0 /frameworks/base/core/java/android/widget/TextView.javaprivatevoidcheckForRelayout(){if((mLayoutParams.width!LayoutParams.WRAP_CONTENT||(mMaxWidthModemMinWidthModemMaxWidthmMinWidth))(mHintnull||mHintLayout!null)(mRight-mLeft-getCompoundPaddingLeft()-getCompoundPaddingRight()0)){......}else{nullLayouts();//在这里调用了requestLayout()requestLayout();invalidate();}}调用View.requestLayout() , 关于requestLayout()可以查看View.requestLayout()使用demoView.requestLayout()源码有干了啥Android 6.0 /frameworks/base/core/java/android/view/View.javapublicvoidrequestLayout(){if(mMeasureCache!null)mMeasureCache.clear();// AttachInfo#mViewRequestingLayout用来追踪最开始发起requestLayout的View。if(mAttachInfo!nullmAttachInfo.mViewRequestingLayoutnull){// Only trigger request-during-layout logic if this is the view requesting it,// not the views in its parent hierarchyViewRootImplviewRootgetViewRootImpl();if(viewRoot!nullviewRoot.isInLayout()){if(!viewRoot.requestLayoutDuringLayout(this)){return;}}mAttachInfo.mViewRequestingLayoutthis;}mPrivateFlags|PFLAG_FORCE_LAYOUT;mPrivateFlags|PFLAG_INVALIDATED;if(mParent!null!mParent.isLayoutRequested()){//这里是核心代码mParent.requestLayout();}if(mAttachInfo!nullmAttachInfo.mViewRequestingLayoutthis){mAttachInfo.mViewRequestingLayoutnull;}}View.requestLayout会调用mParent.requestLayout();也就是执行父类的requestLayout方法。ConstraintLayout 有没有重写requestLayout方法有重写但是还是调用父类ViewGroup的requestLayout最终调用的还是View.requestLayout会。publicvoidrequestLayout(){this.markHierarchyDirty();super.requestLayout();}ConstraintLayout的父布局是是DecorView。Android6.0上DecorView是PhoneWindow的内部类源码路径/frameworks/base/core/java/com.android.internal.policy.PhoneWindow.javapublicclassPhoneWindowextendsWindowimplementsMenuBuilder.Callback{......privatefinalclassDecorViewextendsFrameLayoutimplementsRootViewSurfaceTaker{}......}Android DecorView 一窥全貌上DecorView有没有重写requestLayout方法没有。DecorView继承自FrameLayoutFrameLayout继承自ViewGroup, ViewGroup继承自View所以DecorView还是调用的View的requestLayout方法。核心代码仍然是mParent.requestLayout();DecorView的mParent是谁ViewRootImpl核心代码如下。ViewRootImpl的setView方法通过view.assignParent(this);把自身传递给DecorView作为DecorView的mParent。 这个view就是DecorView//ViewRootImpl构造方法publicViewRootImpl(UiContextContextcontext,Displaydisplay,IWindowSessionsession,booleanuseSfChoreographer){...//持有主线程即创建ViewRootImpl的线程mThreadThread.currentThread();//初始化AttachInfo内部传递持有了ViewRootImpl后续的代码里它将被传递给View所以可以通过View获取到mAttachInfo进而获取到ViewRootImpl。mAttachInfonewView.AttachInfo(mWindowSession,mWindow,display,this,mHandler,this,context);}ViewRootImplpublicvoidsetView(Viewview,WindowManager.LayoutParamsattrs,ViewpanelParentView,intuserId){synchronized(this){if(mViewnull){//持有DecorViewmViewview;...//AttachInfo持有DecorViewmAttachInfo.mRootViewview;//调用requestLayoutrequestLayout();...//通过WindowSession来完成window最终的添加该过程mWindowSession类型是IWindowSession它是一个Binder对象真正的实现类是Session所以这其实是一次IPC的过程远程的调用了Session的addToDisPlay方法。resmWindowSession.addToDisplayAsUser(mWindow,mWindowAttributes,getHostVisibility(),mDisplay.getDisplayId(),userId,mInsetsController.getRequestedVisibility(),inputChannel,mTempInsets,mTempControls);...//将自己传递给View的mParent变量如此调用View相关的invalidate方法就直接调用到了ViewRootImplview.assignParent(this);...}}//View.java的assignParent方法voidassignParent(ViewParentparent){if(mParentnull){mParentparent;}elseif(parentnull){mParentnull;}else{thrownewRuntimeException(view this being added, but it already has a parent);}}所以requestLayout()方法最终调用了ViewRootImpl.requestLayout方法。DecorView的mParent具体是怎么被赋值的过程很长参考关于View中mParent的来龙去脉ViewRootImpl.requestLayout又干了啥OverridepublicvoidrequestLayout(){if(!mHandlingLayoutInLayoutRequest){checkThread();mLayoutRequestedtrue;scheduleTraversals();}}调用了checkThread()方法voidcheckThread(){if(mThread!Thread.currentThread()){thrownewCalledFromWrongThreadException(Only the original thread that created a view hierarchy can touch its views.);}}checkThread方法就是上面异常的抛出位置。

相关新闻

Unity WebGL性能优化实战:从加载到渲染的完整指南

Unity WebGL性能优化实战:从加载到渲染的完整指南

1. 项目概述:为什么Unity WebGL性能优化是2024年的必修课? 最近在社区里看到不少朋友在讨论Unity项目导出WebGL后的性能问题,尤其是卡顿、加载慢、内存爆掉这些老毛病。我自己手头一个数字孪生项目刚上线WebGL版本,也经历了从“能…

2026/10/4 12:21:17 阅读更多 →
Type Beat音乐工程解析:从音色替换到混音实战指南

Type Beat音乐工程解析:从音色替换到混音实战指南

这次我们来看一个名为“感受律动!”的音乐制作项目,更具体地说,它是一首由 Westwood、Thome、Yung Fazo 等艺术家风格启发的 2000s Swag 类型伴奏(Type Beat),名为“YOLO”,并带有“2026”的标签。对于音乐…

2026/10/10 6:09:38 阅读更多 →
AI与云计算时代下软件工程师的技能重构与职业发展

AI与云计算时代下软件工程师的技能重构与职业发展

1. 行业现状与边界重构的背景 2008年金融危机后,全球科技行业经历了一轮深刻的重构。当时我在硅谷一家中型软件公司担任架构师,亲眼目睹了传统软件工程岗位的大规模调整。十四年后的今天,我们正站在另一个关键转折点——AI、云计算和开源运动…

2026/9/19 8:39:06 阅读更多 →

最新新闻

昆明理工大学MBA复试真题详解:题型拆解与高效备考策略

昆明理工大学MBA复试真题详解:题型拆解与高效备考策略

26考研的时间点其实很微妙。初试刚结束那阵子,大多数人都在放松,觉得笔试考完就算过了大半关。但真正走过完整流程的人都知道,复试才是淘汰率最集中的环节,尤其对于昆明理工大学MBA这类报考热度一直不低的项目来说,复试…

2026/10/13 11:41:16 阅读更多 →
MATLAB热交换器求解器:构建传热-流体-物性全耦合计算闭环

MATLAB热交换器求解器:构建传热-流体-物性全耦合计算闭环

简介:本资源是一个基于MATLAB开发的换热器性能仿真计算工具包,面向能源动力、化工过程及热能工程领域的本科生、研究生与初级工程师,用于快速实现ε-NTU法建模与工况分析。工具包共4个文件,含3个核心MATLAB函数(effect…

2026/10/13 11:41:16 阅读更多 →
Spark+Scala实战:TMDB电影数据清洗与聚合分析全流程

Spark+Scala实战:TMDB电影数据清洗与聚合分析全流程

前阵子一直在补 Spark 的实操,想找个有一定规模、字段又足够丰富的公开数据集练手,翻了很多方向,最后锁定了 TMDB 电影数据。这个数据源在影视分析里很常见,里面有电影评分、票房、制作预算、类型、演员、关键词等一堆信息&#x…

2026/10/13 11:41:16 阅读更多 →
Agent技能设计指南:从单体提示词到高可靠技能库的落地实践

Agent技能设计指南:从单体提示词到高可靠技能库的落地实践

先别急着给Agent写代码,如果你正打算磕“agent-skills”,我强烈建议你先停下来,把几个关键思路理清。这个概念的圈内热度已经持续了不短的时间,很多人把它理解为“给Agent塞几十个工具函数就完事”,但真正做落地的时候…

2026/10/13 11:41:16 阅读更多 →
C++单元测试框架gtest实战:从断言机制到CI集成的完整指南

C++单元测试框架gtest实战:从断言机制到CI集成的完整指南

1. 初识gtest:为什么C开发者都绕不开这个测试框架写了十多年C,我见过太多项目在测试这件事上翻车。早期大家靠assert加打印日志,后来有人写个main函数手动调一遍所有接口,再后来CI流水线跑起来,才发现没有像样的单元测…

2026/10/13 11:41:16 阅读更多 →
【机器人控制】三台非全体移动机器人分散式领跟控制、基于李雅普诺夫的轨迹跟踪以及三角与柱形动态切换编队控制Matlab实现

【机器人控制】三台非全体移动机器人分散式领跟控制、基于李雅普诺夫的轨迹跟踪以及三角与柱形动态切换编队控制Matlab实现

✅作者简介:热爱科研的Matlab仿真开发者,擅长数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和数学建模资料 &…

2026/10/13 11:40:13 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →