各位大佬,问个关于子线程操作 UI 的问题

2019-12-18 11:46:21 +08:00
 enjoyee
tvRight.setOnClickListener {
      Thread(){
        Log.e("yyyyy", "是否主线程:${Looper.myLooper() == Looper.getMainLooper()}")
        tvRight.text = "我是谁"
      }.start()
    }

上面的代码,算不算是子线程操作 UI,子线程更新 UI 不是会报错的么?那个 ViewRoot 的 checkThread 方法,疑惑的是为什么这里是可以正常运行的,点击按钮后文本可以正常修改文字的

10023 次点击
所在节点    Android
5 条回复
nicevar
2019-12-18 11:54:18 +08:00
算,子线程操作 UI 又不是一定会出问题,但是不推荐这么干,因为就算运行正常也是个定时炸弹,在某些场景下就会触发直接崩了
ChenStyle
2019-12-18 17:02:05 +08:00
算是子线程操作了 UI,更新也确实会报错。
不过报错也是看情况,如果在 View 已经完成了初始化,那么肯定会报错。而如果没有在 View 完成初始化下用子线程操作 UI 就不会。
因为 Android 系统碎片化的原因,如果出现了不同的结果,要淡定,换个终端调试……
winterbells
2019-12-18 18:41:04 +08:00
pdog18
2019-12-18 19:18:51 +08:00
@ChenStyle 也并不死活「完成初始化就肯定会报错啦」

在「非创建 ViewRootImpl 的线程」上更新 UI 不报错的方法有很多:
1. 开启硬件加速时只调用了 invalidate() ,是不会触发到 ViewRootImpl#checkThread() 的,那么肯定是不会报错了。
2. 像你这种 TextView 如果宽高是固定状态,更新 UI 时,也会因为 TextView 内部的机制,没有触发 requestLayout,也不会报错。
3. 还有可以先在「创建 ViewRootImpl 的线程」上通过 requestLayout 让 viewparent 的 flag 进行改变,那么那么在其他线程 requestLayout 也不会递归向上传递到 checkThread
4. 在某个线程中(不一定要主线程)通过 WindowManager 创建 ViewRootImpl,这样在那个线程中更新 UI 也不会有任何问题。
pdog18
2019-12-18 19:20:09 +08:00
像你这代码,你把硬件加速关了,然后 TextView 宽高改成 wrap_content 再点一次,这才 100% 崩。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/630091

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX