如何在子线程中更新UI
一:报错情况
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结束之后,又把进入标志改回来。
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的更多相关文章
- Android在子线程中更新UI(二)
MainActivity如下: package cc.testui2; import android.os.Bundle; import android.view.View; import andro ...
- Android在子线程中更新UI(一)
MainActivity如下: package cc.testui1; import android.os.Bundle; import android.os.Handler; import andr ...
- android 不能在子线程中更新ui的讨论和分析
问题描写叙述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是仅仅能在主线程中更改 ui.子线程要 ...
- 使用Handler在子线程中更新UI
Android规定仅仅能在主线程中更新UI.假设在子线程中更新UI 的话会提演示样例如以下错误:Only the original thread that created a view hierach ...
- Android多线程之(一)View.post()源码分析——在子线程中更新UI
提起View.post(),相信不少童鞋一点都不陌生,它用得最多的有两个功能,使用简便而且实用: 1)在子线程中更新UI.从子线程中切换到主线程更新UI,不需要额外new一个Handler实例来实现. ...
- Android开发UI之在子线程中更新UI
转自第一行代码-Android Android是不允许在子线程中进行UI操作的.在子线程中去执行耗时操作,然后根据任务的执行结果来更新相应的UI控件,需要用到Android提供的异步消息处理机制. 代 ...
- C#子线程中更新ui
本文实例总结了C#子线程更新UI控件的方法,对于桌面应用程序设计的UI界面控制来说非常有实用价值.分享给大家供大家参考之用.具体分析如下: 一般在winform C/S程序中经常会在子线程中更新控件的 ...
- Android 在子线程中更新UI
今天在做练习时,在一个新开启的线程中调用“Toast.makeText(MainActivity.this, "登陆成功",Toast.LENGTH_SHORT).show();” ...
- 老问题:Android子线程中更新UI的3种方法
在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 方法一:用Handler 1.主线程中定义Handler: Handle ...
随机推荐
- kubernetes基本概念 pod, service
k8s的部署架构 kubernetes中有两类资源,分别是master和nodes,master和nodes上跑的服务如下图, kube-apiserver | kubelet kube-contro ...
- Linux下编译tinyxml生成动态库
首先去到sourceforge下载tinyxml的源码,https://sourceforge.net/projects/tinyxml/?source=dlp,最新版本是2.6.2. 将下载成功的t ...
- python实现调用摄像头或打开视频文件
目录: (一)调用摄像头或打开视频文件代码实现 (二)说明和补充 (一)调用摄像头或打开视频文件代码实现 1 # -*- coding=GBK -*- 2 import cv2 as cv 3 4 5 ...
- PAT A1039、A1047——vector常见用法
vector 常用函数实例 (1)push_back() (2)pop_back() (3)size() (4)clear():清空vector中所有元素 (5)insert():insert(it, ...
- VS2013中using System.Windows.Forms;引用不成功
命名空间"System"中不存在类型或命名空间名称"Windows" 项目右侧--解决资源管理器---引用---右键--添加引用---在.NET下拉框找---找 ...
- [cf587D]Duff in Mafia
二分最大边权,即有些边强制不能被选 接下来,即任意一点上某两边不能同时被选,以及任意一点上颜色相同的两边必须被选择一条 这些限制都可以用2-sat的形式来描述(强制不能选即连边"选-> ...
- 填坑总结:python内存泄漏排查小技巧
摘要:最近服务遇到了内存泄漏问题,运维同学紧急呼叫解决,于是在解决问题之余也系统记录了下内存泄漏问题的常见解决思路. 本文分享自华为云社区<python内存泄漏排查小技巧>,作者:luti ...
- 面试官又整新活,居然问我for循环用i++和++i哪个效率高?
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 前几天,一个小伙伴告诉我,他在面试的时候被面试官问了这么一个问题: 在for循环中,到底应该用 i++ 还是 ++i ? 听到这,我感觉这面试官 ...
- 论文解读(node2vec)《node2vec Scalable Feature Learning for Networks》
论文题目:<node2vec Scalable Feature Learning for Network>发表时间: KDD 2016 论文作者: Aditya Grover;Adit ...
- mybatis-批量操作数据(list对象 )
在实际工作中老是忘记 传入的参数和数据库参数名称要一致还是与实体类型一致导致很多笑话发生. 那我还是做个记录吧! dao层: int addRemark(@Param("list" ...