在Android中,非主线程不能更新UI
来源:互联网 发布:mac怎么输入罗马数字 编辑:程序博客网 时间:2024/05/11 15:15
上一篇文章《Anroid异步消息机制(Handler、Looper、Message、MessageQueue)以及ThreadLocal运用》提到,在Android中,非主线程不能更新UI(ViewRootImpl在主线程中创建,所以我们要在主线程中更新UI。同理,如果ViewRootImpl在子线程中创建的话,那么也可以在子线程中更新UI,也就是说在哪里更新UI和ViewRootImpl在哪里创建是关联的。默认ViewRootImpl在主线程中创建),这时候我们可以借助Handler来实现(Activiy.runOnUiThread()也可以实现,但原理也是Handler,调用的post(Runnable))“。
一、我们做个测试
1、activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <TextView android:id="@+id/main_thread" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UI主线程" /> <TextView android:id="@+id/sub_thread" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UI主线程中直接建立子线程" /> <TextView android:id="@+id/sub_thread_thread" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UI主线程中直接建立子线程的子线程" /> <TextView android:id="@+id/click_window" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="直接更新UI线程" /> <TextView android:id="@+id/click_subclass_window" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="直接建立子类更新UI线程并且跳出子窗口" /> <TextView android:id="@+id/click_thread" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击,直接建立子线程" /> <TextView android:id="@+id/click_subclass_thread" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击,建立子类,子类中建立子线程" /> <Button android:id="@+id/click_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击,直接更新UI线程" /> <Button android:id="@+id/click_subclass_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击,直接建立子类更新UI线程并且跳出子窗口" /> <Button android:id="@+id/click_thread_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击,直接建立子线程" /> <Button android:id="@+id/click_subclass_thread_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击,建立子类,子类中建立子线程" /> <Button android:id="@+id/click_windowManager_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击,建立子窗口" /></LinearLayout>
2、MainActivity.java
3、结果点击“点击,直接更新UI进程”,结果图如下:
点击“点击,直接建立子类更新UI线程并且跳出子窗口”,如下图所示:
当点击“点击,直接建立子线程”和“点击,建立子类,子类中建立子线程”按钮,应用崩溃报错,如下:
从结果中可以看出,sub_thread、sub_thread_thread、click_window、click_subclass_window对应的操作可以正常更新UI;但点击按钮“点击,直接建立子线程”和“点击,建立子类,子类中建立子线程”按钮的时候,应用崩溃报错(内部类中调用)。从错误日志,可以看出调用顺序
TextView.setText()->TextView.checkForRelayout()->View.requestLayout()->ViewRootImpl.requestLayout(()->ViewRootImpl.checkThread()。
1、TextView (继承View)
2、View3、ViewRootImpl (实现接口ViewParent)从checkThread(),当当前线程不是创建ViewRootImpl的原始线程的时候,报出“Only the original thread that created a view hierarchy can touch its views.”错误,意思很明了,即只有创建视图的源线程才能更改它的视图。那如何在非UI线程中更新UI线程?二、非UI线程中更新UI线程
1、Handler异步消息模式
2、创建新的ViewRootImpl
比如WindowManager
在我们的测试中,点击“点击,建立子窗口”就是实现子线程中利用WindowManager建立新的ViewRootImpl,点击按钮,出现应用崩溃,报错如下图:
根据报错跟踪具体代码,分析如下:
1、WindowManager接口(实现接口ViewManager)
2、WindowManagerImpl(实现接口WindowManager)
3、WindowManagerGlobal从上面内容与ViewRootImpl源代码分析,我们知道ViewRootImpl会创建变量ViewRootHandler mHandler,这是Handler对象;从文章《Anroid异步消息机制(Handler、Looper、Message、MessageQueue)以及ThreadLocal运用》,我们知道新建Handler,需要调用Looper.myLooper(),它会检查当前线程是否有Looper存在,如果没有,就报错,提示我们需要通过走新建Looper的流程(Looper.prepare()->Looper.loop(),具体可以看文章)。故需要在WindowManager建立前加上Looper.prepare(),建立后加上Looper.loop(),具体看代码中注释部分。
三、Android视图
从ViewRootImpl到WindowManger源码分析,可以猜测每个Activity可以有多个ViewRootImpl,通过WindowManager.addView(View view, ViewGroup.LayoutParams params)将View mView添加到新建的ViewRootImpl中;view的逻辑与事件都会一层层上到ViewRootImpl来处理;各个ViewRootImpl是相互独立的。我们可以推导出WindowManger、ViewRootImpl、View、Activity等之间的关系,如下图所示:
四、遗留问题
1、requestLayout()、invalidate()有何区别?
requestLayout分为三步:测量(测宽、高),布局(坐标),绘制
invalidate:UI线程,进行绘制
postInvalidate:非UI线程通过Handler更新UI
2、为什么sub_thread、sub_thread_thread对应的操作可以正常更新UI,他们跳过了checkThread()?
- 在Android中,非主线程不能更新UI
- Android 在非主线程中更新UI也能成功原因详解
- Android禁止在非Activity主线程中更新UI的解决办法
- Android非主线程更新UI
- Android 在子线程中更新主线程UI
- Android 更新UI 只能在主线程?
- Android显示系统之View与SurfaceView更新屏幕的区别.对于View.则是在UI主线程中更新画面.SurfaceView更新屏幕,是在非UI线程(主线程)中实现SurfaceHolde
- android4.4中关于不能再主线程下载和在主线程中更新UI的矛盾
- 在非主线程是否可以更新UI
- Android 关于非主线程不能操作UI的认识
- android学习记录(十一)在非主线程实现更新ui------------Handler
- 在主线程更新UI
- Android中在主线程与在子线程中更新UI的探索
- 在非Activity(非主线程)中更新UI的办法:(一)Handler+Thread+Activity静态成员变量
- 在非Activity(非主线程)中更新UI的办法:(二)Handler+接口回调
- ProgressDialog不能在非主线程中show
- android:在非主线程中不能够使用Toast.makeText
- android 橘子汁 在子线程中更新主线程UI 困扰
- spring加载xsd的机制详解(Failed to read schema document的解决办法)
- Android 友盟sdk6.3.0 分享,授权登陆详解
- Python生成数组
- javaweb开发之EL表达式
- Hive基本语法操练
- 在Android中,非主线程不能更新UI
- 提高Github Clone速度
- 液晶电视的MEMC(运动画质补偿技术)的优势不足
- 安卓编年史:虚拟键盘打开设备设计的大门
- 平衡二叉树判断练习
- 字符串分割--java中String.split()用法
- HTTPS 为什么更安全,先看这些
- JavaScript知识点归纳之教程(一)
- linux基础知识