子线程更新主线程的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/7/28 11:10:18 阅读更多 →
Type Beat音乐工程解析:从音色替换到混音实战指南

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

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

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

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

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

2026/7/28 11:10:18 阅读更多 →

最新新闻

剪映专业版教程:制作立体轮播滚动效果

剪映专业版教程:制作立体轮播滚动效果

前言 今天教大家一个立体轮播滚动效果。这种效果利用立方体转场让图片以3D立体方式切换,配合流动荧光和简约边框,营造出流畅的轮播体验。 效果预览:多张图片以立方体旋转的方式依次切换,带有流动荧光特效和简约边框,…

2026/7/28 11:22:25 阅读更多 →
3分钟掌握浏览器请求控制:Header Editor完全指南

3分钟掌握浏览器请求控制:Header Editor完全指南

3分钟掌握浏览器请求控制:Header Editor完全指南 【免费下载链接】HeaderEditor Manage browsers requests, include modify the request headers, response headers, response body, redirect requests, cancel requests 项目地址: https://gitcode.com/gh_mirro…

2026/7/28 11:22:25 阅读更多 →
League Akari:基于LCU API的模块化英雄联盟客户端自动化框架

League Akari:基于LCU API的模块化英雄联盟客户端自动化框架

League Akari:基于LCU API的模块化英雄联盟客户端自动化框架 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit League Akari是一款基…

2026/7/28 11:22:25 阅读更多 →
比赛组织上有不少问题,估计跟重视度不够有关

比赛组织上有不少问题,估计跟重视度不够有关

Trae创造性大赛:奖励发放异常、模型锁定问题解析,附大型活动组织经验 作为字节Trae创造性大赛的复赛参赛选手。全程完整参与赛事、提交作品完成参赛流程,原本只是正常等待赛事奖励发放,却在赛后遇到了很多参赛者都疑惑的账号权限降…

2026/7/28 11:22:25 阅读更多 →
Gemini Spark日历PDF自动导入:AI解析与批量处理实践

Gemini Spark日历PDF自动导入:AI解析与批量处理实践

这次我们来看一个很实用的功能更新——Gemini Spark 新增的日历 PDF 自动导入功能。如果你经常需要处理会议安排、课程表或者各种日程 PDF 文档,这个功能应该能帮你省下不少手动输入的时间。 简单来说,Gemini Spark 是 Google 推出的 AI 助手工具&#…

2026/7/28 11:22:24 阅读更多 →
Dify工作流与MCP服务:打造企业级岗位专属AI副驾实战指南

Dify工作流与MCP服务:打造企业级岗位专属AI副驾实战指南

上周,我帮一个做电商运营的朋友解决了一个问题。他们团队每天要处理大量的商品文案、营销话术和客服问答模板,每个岗位的同事都希望有一个能理解自己工作内容的AI助手。他们试过直接给ChatGPT提要求,也用过一些现成的AI工具,但效果总是不尽如人意:要么生成的文案不符合平台…

2026/7/28 11:21:24 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/27 4:33:59 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻