子线程更新主线程的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方法就是上面异常的抛出位置。

相关新闻

React通过ref获取子组件的数据和方法

React通过ref获取子组件的数据和方法

一、函数式组件父组件1) ref必须传值, 否则childRef拿不到子组件的数据和方法注意: 不一定使用app组件, 其他的任何父组件都可以import "./App.css"; import React, { useEffect, useRef } from "react"; import RefToGetChild from "./components/Re…

2026/7/28 11:09:10阅读更多 →
Postman集成ForgeJS实现RSA加密:自动化接口安全测试实战

Postman集成ForgeJS实现RSA加密:自动化接口安全测试实战

1. 项目概述:为什么要在Postman里折腾RSA?如果你经常和需要身份认证或数据加密的API打交道,尤其是那些金融、支付或涉及敏感数据的接口,那么“RSA非对称加密”这个词你一定不陌生。开发文档里经常会要求,在调用某个关键…

2026/7/28 11:09:10阅读更多 →
代码生成工具实践指南:从原理到生产环境集成

代码生成工具实践指南:从原理到生产环境集成

在实际开发过程中,我们经常需要将复杂的业务逻辑或算法封装成可复用的组件。Codex 作为一种代码生成和辅助工具,能够帮助开发者快速生成代码片段、完成函数实现甚至理解复杂代码逻辑。虽然原始材料没有提供具体的技术细节,但我们可以基于常见…

2026/7/28 11:09:10阅读更多 →
ArkPets桌面宠物技术指南:明日方舟角色动态桌面集成方案

ArkPets桌面宠物技术指南:明日方舟角色动态桌面集成方案

ArkPets桌面宠物技术指南:明日方舟角色动态桌面集成方案 【免费下载链接】Ark-Pets Arknights Desktop Pets | 明日方舟桌宠 (ArkPets) 项目地址: https://gitcode.com/gh_mirrors/ar/Ark-Pets ArkPets是一款基于Java开发的明日方舟角色桌面宠物应用&#xf…

2026/7/29 0:57:59阅读更多 →
数据挖掘不靠运气,libsvm python一出手,隐藏信息全暴露

数据挖掘不靠运气,libsvm python一出手,隐藏信息全暴露

数据挖掘, 它是一种过程, 这个过程是从数量众多的数据里头, 借助算法去搜寻隐匿在其中的信息。数据挖掘常常跟计算机科学存在关联, 借助统计, 通过在线分析处理, 利用情报检索, 运用机器学习, 依靠按过去经验法则设计的专家系统, 再加上模式识别等众多方法, 去达成上述那个目标…

2026/7/29 0:57:59阅读更多 →
老Mac升级终极指南:3个简单步骤让旧设备运行最新macOS

老Mac升级终极指南:3个简单步骤让旧设备运行最新macOS

老Mac升级终极指南:3个简单步骤让旧设备运行最新macOS 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 你是否还在为老Mac无法升级最新系统而烦恼&…

2026/7/29 0:57:59阅读更多 →
粉笔直播课适合税务系统考试突破吗

粉笔直播课适合税务系统考试突破吗

本文面向报考国家税务总局及各省税务局(国税/地税合并后统称税务系统)岗位、进入备考中后期且分数长期停滞的考生,围绕"突破瓶颈"这一核心诉求,评估粉笔直播课对税务系统考试的适配性。文中数据来源于公开财报、官网公示…

2026/7/29 0:57:59阅读更多 →
粉笔直播课适合气象局招聘突破吗

粉笔直播课适合气象局招聘突破吗

本文面向准备参加气象部门公开招聘(含国家气象局及地方气象局事业编、参公岗位)的考生,围绕"突破备考瓶颈"这一核心诉求,对粉笔直播课在该类考试中的适配性做客观拆解。文中数据来源于公开财报、官网公示价格、第三方投…

2026/7/29 0:57:59阅读更多 →
Silk v3解码器终极指南:3种方法轻松转换微信QQ语音文件为MP3

Silk v3解码器终极指南:3种方法轻松转换微信QQ语音文件为MP3

Silk v3解码器终极指南:3种方法轻松转换微信QQ语音文件为MP3 【免费下载链接】silk-v3-decoder [Skype Silk Codec SDK]Decode silk v3 audio files (like wechat amr, aud files, qq slk files) and convert to other format (like mp3). Batch conversion support…

2026/7/29 0:55:58阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“! 在构建复杂的 Agent 系统时,我们经常会遇到这样的场景:Agent 正在执行一个多步骤的任务,比如“下单购买商品”,但执行到一半时,我们…

2026/7/29 0:01:46阅读更多 →
自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

近日,国际专注开放式技术研发的声学品牌Nank南卡,正式官宣实力艺人曾舜晞担任品牌代言人。消息一经发出便轰动全网。为什么耳机品牌不选择流量明星、老牌歌手?而且是选择曾舜晞?让我们一起来探索一下!比起短期的流量&a…

2026/7/29 0:01:46阅读更多 →
【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:01:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/28 20:22:24阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →