Android为什么不能在子线程更新UI

简介: Android为什么不能在子线程更新UI

Android为什么不能在子线程更新UI?

// Android中为什么子线程不能更新UI?

viewRootImpl对象是在Activity中的onResume方法执行完成之后,View变得可见时才创建的,之前的操作是没有进行线程检查的,所以没有报错。但是ViewRootImpl创建之后,由于进行了checkThread操作,所以就不能在子线程更改UI了

当访问 UI 时,ViewRootImpl 会调用 checkThread方法去检查当前访问 UI 的线程是否为创建 UI 的那个线程,如果不是。则会抛出异常

如果不做这个校验,是不是我也可以正常在子线程更新UI

// 如果不做这个校验,是不是我也可以正常在子线程更新UI?

按理来说,这样是可以的

但是google为什么要这样去设计呢

// 但是google为什么要这样去设计呢

如果在不同的线程去控制用一个控件,由于网络延时或者大量耗时操作,会使UI绘制错乱,出了问题也很难去排查到底是哪个线程更新时出了问题;

主线程负责更新,子线程负责耗时操作,能够大大提高响应效率

UI线程非安全线程,多线程进行并发访问有可能会导致内存溢出,降低硬件使用寿命;且非线程安全不能加Lock线程锁,否则会阻塞其他线程对View的访问,效率就会变得低下!

ViewRootImp是在onActivityCreated方法后面创建的吗

// ViewRootImp是在onActivityCreated方法后面创建的吗?

ViewRootImpl是在Activity的onResume()方法后面创建出来的,所以在onResume之前的UI更新可以在子线程操作而不报错,因为这个时候ViewRootImpl还没有创建,没有执行checkThread()方法。

安卓系统中,操作viwe对象没有加锁,所以如果在子线程中更新UI,会出现多线程并发的问题,导致页面展示异常。


为什么一定需要checkThread呢

// 为什么一定需要checkThread呢?

因为UI控件不是线程安全的

那为什么不加锁呢

// 那为什么不加锁呢?

  • 一是加锁会让UI访问变得复杂;
  • 二是加锁会降低UI访问效率,会阻塞一些线程访问UI。
  • 所以干脆使用单线程模型处理UI操作,使用时用Handler切换即可

为什么一开始在Activity的onCreate方法中创建一个子线程访问UI,程序还是正常能跑起来呢

// 为什么一开始在Activity的onCreate方法中创建一个子线程访问UI,程序还是正常能跑起来呢?

因为ViewRootImpl 的创建在 onResume 方法回调之后,而我们一开篇是在 onCreate 方法中创建了子线程并访问 UI,在那个时刻,ViewRootImpl 还没有创建,我们在因此 ViewRootImpl#checkThread 没有被调用到,也就是说,检测当前线程是否是创建的 UI 那个线程 的逻辑没有执行到,所以程序没有崩溃一样能跑起来。而之后修改了程序,让线程休眠了 3000 毫秒后,程序就崩了。很明显 3000 毫秒后 ViewRootImpl 已经创建了,可以执行 checkThread 方法检查当前线程

Android中子线程真的不能更新UI吗

// Android中子线程真的不能更新UI吗?

任何线程都可以更新自己创建的UI,但是需要满足各自对应的条件


ViewRootImpl 还没创建出来之前。UI 修改的操作没有线程限制。因为 checkThread 方法不会被执行到。

在 ViewRootImpl 创建完成之后,保证「创建 ViewRootImpl 的操作」和「执行修改 UI 的操作」在同一个线程即可。也就是说,要在同一个线程调用 ViewManager的addView 和 ViewManager的updateViewLayout 方法。

// 注:ViewManager 是一个接口,WindowManger 接口继承了这个接口,我们通常都是通过 WindowManger(具体实现为 WindowMangerImpl) 进行 view 的 add remove update 操作的。

对应的线程需要创建 Looper 并且调用 Looper的loop 方法,开启消息循环。

保证上述条件1成立不就可以避免checkThread时候抛出异常了吗?为什么还需要开启消息循坏

// 保证上述条件1成立,不就可以避免checkThread时候抛出异常了吗?为什么还需要开启消息循坏?

条件 1 可以避免检查异常,但是无法保证 UI 可以被绘制出来。

条件 2 可以让更新的 UI 效果呈现出来。

注:WindowManger的addView 最终会调用WindowManageGlobal的addView方法,进而触发ViewRootImpl的setView 方法,该方法内部会调用ViewRootImpl的requestLayout 方法。根据 UI 绘制原理,下一步就是 scheduleTraversals了,该方法会往消息队列中插入一条消息屏障,然后调用 Choreographer的postCallback 方法,往 looper 中插入一条异步的 MSG_DO_SCHEDULE_CALLBACK 消息。等待垂直同步信号回来之后执行

使用子线程更新UI有实际应用场景吗

// 使用子线程更新 UI 有实际应用场景吗?

Android 中的 SurfaceView 通常会通过一个子线程来进行页面的刷新。如果我们的自定义 View 需要频繁刷新,或者刷新时数据处理量比较大,那么可以考虑使用 SurfaceView 来取代 View


目录
相关文章
|
27天前
|
XML IDE 开发工具
【Android UI】自定义带按钮的标题栏
【Android UI】自定义带按钮的标题栏
31 7
【Android UI】自定义带按钮的标题栏
|
8天前
|
Android开发 开发者
Android UI设计中,Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等,定义在`styles.xml`。
【6月更文挑战第26天】Android UI设计中,Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等,定义在`styles.xml`。要更改主题,首先在该文件中创建新主题,如`MyAppTheme`,覆盖所需属性。然后,在`AndroidManifest.xml`中应用主题至应用或特定Activity。运行时切换主题可通过重新设置并重启Activity实现,或使用`setTheme`和`recreate()`方法。这允许开发者定制界面并与品牌指南匹配,或提供多主题选项。
19 6
|
8天前
|
开发工具 Android开发 开发者
Android `.9.png` 图像是用于UI的可拉伸格式,保持元素清晰度和比例
【6月更文挑战第26天】Android `.9.png` 图像是用于UI的可拉伸格式,保持元素清晰度和比例。通过边上的黑线定义拉伸区域,右下角黑点标识内容区域,适应文本或组件大小变化。常用于按钮、背景等,确保跨屏幕尺寸显示质量。Android SDK 提供`draw9patch.bat`工具来创建和编辑。**
26 6
|
8天前
|
API Android开发 开发者
`RecyclerView`是Android API 21引入的UI组件,用于替代ListView和GridView
【6月更文挑战第26天】`RecyclerView`是Android API 21引入的UI组件,用于替代ListView和GridView。它提供高效的数据视图复用,优化的布局管理,支持多种布局(如线性、网格),并解耦数据、适配器和视图。RecyclerView的灵活性、性能(如局部刷新和动画支持)和扩展性使其成为现代Android开发的首选,特别是在处理大规模数据集时。
20 2
|
9天前
|
Android开发 开发者
Android UI中的Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等。要更改主题
【6月更文挑战第25天】Android UI中的Theme定义了Activity的视觉风格,包括颜色、字体、窗口样式等。要更改主题,首先在`styles.xml`中定义新主题,如`MyAppTheme`,然后在`AndroidManifest.xml`中设置`android:theme`。可应用于全局或特定Activity。运行时切换主题需重置Activity,如通过`setTheme()`和`recreate()`方法。这允许开发者定制界面以匹配品牌或用户偏好。
15 2
|
27天前
|
XML Android开发 数据格式
【Android UI】使用RelativeLayout与TableLayout实现登录界面
【Android UI】使用RelativeLayout与TableLayout实现登录界面
30 5
|
27天前
|
XML Android开发 数据格式
【Android UI】中间对齐UI组件
【Android UI】中间对齐UI组件
13 1
|
Android开发
《Android UI基础教程》——导读
本节书摘来自异步社区《Android UI基础教程》一书中的目录,作者 【美】Jason Ostrander,更多章节内容可以访问云栖社区“异步社区”公众号查看
778 0