一:报错情况

 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:8798)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1606)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at android.view.View.requestLayout(View.java:25390)
at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3593)
at android.view.View.requestLayout(View.java:25390)
at android.widget.TextView.checkForRelayout(TextView.java:9719)
at android.widget.TextView.setText(TextView.java:6311)

我尝试在子线程中更新UI:

binding.textView.setOnClickListener {
thread {
(it as TextView).text = "ldkjfla;66666sdf"
}
}

二:报错原因

首先,我们更新UI,会调用text view的request layout方法, 然后view 的request layout方法又会调用到它父view的 request layout方法:

子view request layout  ------>    父view request layout

这样一层层调用上去,因为view系统的最上层是一个叫作view root impl的view,所以最终会调用到它的request layout方法。

我们来看看它的request layout方法,然后看看有没有什么对策:

@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread(); 和之前报错代码的最上面对应起来了
mLayoutRequested = true;
scheduleTraversals();
}
}

注意啦注意啦!!当在子view中更新UI,就会调用到view root impl的request layout方法!!然后里面会调用check thread方法来看看更新UI的线程等不等于view root impl创建时的线程!!

view root impl是在activity的onResume生命周期主线程中创建的。所以这个checkThread就不通过啦!!

三:解决办法

首先,上面说,更新UI时,会调用request layout方法一层层调用上去,那我们去看看这个路径,看看有没有办法斩断这个路径。

1:进入text view的set text方法

2:进入check for relayout方法

3:进入 request layout方法

4:然后就进入了view的request layout方法之中

然后接下来的过程就是我刚刚说的,一直向上调用到view root impl的request layout最后报错了。注意啦!!注意啦!!在判断向不向上去传递调用request layout的时候,会看看

!mParent.isLayoutRequested()

如果我们让view root impl的LayourRequested参数为true,然后表达式为false,就不会调用到view root impl的requst layout方法,就不会check thread了。

所以我们要让这个参数为true!!!

四:让这个参数为true

我们可以注意到, view root impl的request layout方法中,在check thread之后,顺手就把这个参数置为true了。所以我们可以调用一次textview的request layout方法,然后调用到view root impl的方法,把它置为true,然后我们再在子线程更新UI,就不会进入view root impl的request layout方法了。

binding.textView.setOnClickListener {
it.requestLayout()//因为在主线程,所以最后check thread没有事
thread {
(it as TextView).text = "ldkjfla;66666sdf"
}
}

五:LayoutRequested的意义

我们更新UI,就会调用到view root impl的request layout方法,在check thread之后,就会把Layoutrequested置为true!!表示自己正处于被请求重新去布局的状态!!置为true,之后,下一个方法就是鼎鼎有名的

scheduleTraversals()

它会执行view root impl的performTraversal!!  对整个view tree进行从上到下的测量、布局和绘制!!在这个perform traversal结束时,会把LayoutRequested置为false。不然,下一次更新UI不就会没办法到这个方法然后失败了嘛!!
所以为了性能原因,在打算开展一个perform traversal之前,会把进入标志改一下,perform traversal结束之后,又把进入标志改回来。

我们就是趁它标志位为true的时候更改UI.
要是它perform traversal结束之后把标志位改了回来,,,那还是会报错。
binding.textView.setOnClickListener {
it.requestLayout()//因为在主线程,所以最后check thread没有事
thread {
sleep(1000)//等了一秒, performTraversal结束之后标志位恢复,又会去到check thread了。
(it as TextView).text = "ldkjfla;66666sdf"
}
}

番外:

对于text view,在checkForRelayout方法中,会看看宽高是否改变然后决定是否向上传递request layout,所以,如果一个text view固定宽高,即使不主动request layout,在子线程中也可以修改文字不报错!!试一试!!

如何在子线程中更新UI的更多相关文章

  1. Android在子线程中更新UI(二)

    MainActivity如下: package cc.testui2; import android.os.Bundle; import android.view.View; import andro ...

  2. Android在子线程中更新UI(一)

    MainActivity如下: package cc.testui1; import android.os.Bundle; import android.os.Handler; import andr ...

  3. android 不能在子线程中更新ui的讨论和分析

    问题描写叙述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是仅仅能在主线程中更改 ui.子线程要 ...

  4. 使用Handler在子线程中更新UI

    Android规定仅仅能在主线程中更新UI.假设在子线程中更新UI 的话会提演示样例如以下错误:Only the original thread that created a view hierach ...

  5. Android多线程之(一)View.post()源码分析——在子线程中更新UI

    提起View.post(),相信不少童鞋一点都不陌生,它用得最多的有两个功能,使用简便而且实用: 1)在子线程中更新UI.从子线程中切换到主线程更新UI,不需要额外new一个Handler实例来实现. ...

  6. Android开发UI之在子线程中更新UI

    转自第一行代码-Android Android是不允许在子线程中进行UI操作的.在子线程中去执行耗时操作,然后根据任务的执行结果来更新相应的UI控件,需要用到Android提供的异步消息处理机制. 代 ...

  7. C#子线程中更新ui

    本文实例总结了C#子线程更新UI控件的方法,对于桌面应用程序设计的UI界面控制来说非常有实用价值.分享给大家供大家参考之用.具体分析如下: 一般在winform C/S程序中经常会在子线程中更新控件的 ...

  8. Android 在子线程中更新UI

    今天在做练习时,在一个新开启的线程中调用“Toast.makeText(MainActivity.this, "登陆成功",Toast.LENGTH_SHORT).show();” ...

  9. 老问题:Android子线程中更新UI的3种方法

    在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 方法一:用Handler 1.主线程中定义Handler: Handle ...

随机推荐

  1. Java设计模式之(五)——代理模式

    1.什么是代理模式 Provide a surrogate or placeholder for another object to control access to it. Proxy Patte ...

  2. [loj3340]命运

    容斥,强制若干条链不重要,即有$2^{n-1-s}$种(其中$s$为这些链的并所覆盖的边数),暴力将选中的链打标记,时间复杂度$o(m^{2}2^{m}+n\log_{2}n)$(预处理出这$2m$个 ...

  3. IE 跨域设置

    开发的时候会发现IE下跨域无法访问,报错: Failed to load resource: net::ERR_CONNECTION_REFUSED 解决方法有两种: 自己写代理服务,访问代理服务,代 ...

  4. CentOS编译openjdk

    编译openjdk 1. 下载openjdk源码 openjdk的官网是OpenJDK (java.net) 在网站左侧就能看到它的源码位置的链接 从图上可以看到,它的源码在两个位置有托管,Mercu ...

  5. CF1373G

    考虑中间格子不能有相同的点,其实是没用的. 其唯一用处是用来规定最后的是无法重叠的. 我们可以证明最后位置的无重叠和中间不重叠是充要的. 那显然可以我们对每个点往后连边: 形式的话的说: 对 \((x ...

  6. Eigensoft-smartpca分析PCA报错:warning (mapfile): bad chrom: Segmentation fault

    目录 问题 解决 问题 一直以来用Eigensoft的smartpca来做群体遗传的PCA分析很顺畅,结果也比较靠谱. 但今天报错如下: $ ~/miniconda3/bin/smartpca -p ...

  7. fluidity install on Cluster

    fluidity install on Cluster 1.编译器安装 gcc4.6 openmpi python HOME_PATH= /hpcdata1/xiaozhong/ 1.1.gmp &a ...

  8. android listview展示图片

    最近学习android开发,感触颇多,和网站开发对比,还是有很大的差距,在这里记录一下. android listview展示图片 在网站开发上,展示图片非常简单,一个HTML img标签就搞定,加上 ...

  9. WPS表格数据透视表的美化和布局

    设计--分类汇总--在组的底部显示所有分类汇总   把二级分类单独放在一类中 设计--报表布局--以表格形式显示   快速调整表格的外观 分析--+/-按钮   设置字段的数字格式以万元为单位 选中任 ...

  10. javaSE高级篇3 — 网络编程 — 更新完毕

    网络编程基础知识 先来思考两个问题( 在这里先不解决 ) 如何准确的找到一台 或 多台主机? 找到之后如何进行通讯? 网络编程中的几个要素 IP 和 端口号 网络通讯协议:TCP / UDP 最后一句 ...