为什么子线程不能更新UI的完全解析!!
来源:互联网 发布:常熟小商品淘宝摄影 编辑:程序博客网 时间:2024/05/17 01:19
前言
————————————————————————————————————————————————————
为什么Andorid不能更新子线程更新UI?这里我们从几个方面来探究;
———————————————————————————————————————————————————
1:Android开发人员设计角度
由于Android是典型的GUI(图形用户界面)模型,而现代GUI模型都是单线程设计思想(读者可自行查询相关资料)。大意就是为了避免获取数据时候的竞争导致死锁。多线程问题已经够麻烦了,如果GUI也设计程多线程,肯定是会更加麻烦。
2:SDK源码角度
1:如果子线程更新了UI,我们会得到异常提示,跟进异常抛出类查看,此方法是ViewRootImpl类内部的方法:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); }}
通过mThread 与 Thread.currentThread()相等来判断是否异常,,
所以重点来了Thread.currentThread()是当前线程,那么mThread是什么线程呢?
2:我们跟进查看构造方法
public ViewRootImpl(Context context, Display display) { ...... mThread = Thread.currentThread(); ......}可知道当ViewRootimpl第一次构造时候就分发给了我们一个currentThread(). 那么创建ViewRootimpl与更新checkThread的线程就必须是一样的才不会有异常。那么我们创建ViewRootImpl的线程是什么线程呢?如下分析
ViewRootImpl分析
官方对于此类解释:
The top of a view hierarchy, implementing the needed protocol between View* and the WindowManager.
This is for the most part an internal implementation* detail of {@link WindowManagerGlobal}.
ViewRootImpl他是View树的顶级视图,但它却又不是View,只是实现了View与WindowManager之间的通信协议(2.2版本之前使用ViewRoot此后版本更改为ViewRootImpl)。
这里我们要了解就是ViewRootImpl并不是一个View,只是一个将View与WindowManager关联起来的类;分析得:ViewRootImpl也是依附于View与windowManager存在的,再通过此ViewRootImpl类注释分析:就是WindowManagerGlobal具体细节上的一个内部实现“This is for the most part an internal implementation detail of @link WindowManagerGlobal” ——————>跟进
WindowManagerGlobal分析:
通过官方注释得
这个类的关系:是底层类与窗口之间的上下文联系类,我们并不需要使用它,直接使用WindowManager;
类注释叫我们 see windowmanager
WindowManager与windowManagerimpl
然而WindowManager是一个接口 实现了ViewManager,ViewManager也是一个接口。
所以我们要看其实现类 windowMangerImpl,
WindowManagerImpl并没有实现WindowManager的三大操作,而是全部交给WindowManagerGlobal来处理,WindowManagerGlobal以工厂的形式向外提供自己的实例。就是说WindowManagerImpl将所有的操作全部委托给WindowManagerGlobal来实现。这是一种桥接模式。
通过windowManagerImpl发现其内部就是对WindowManagerGlobal 的封装,各种addview,removeview之类的调用本意就是WindowManagerGlobal 的调用如
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @Override public void removeView(View view) { mGlobal.removeView(view, false); }@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); }
ViewManager分析
ViewManager就一个简单的接口,定义了addview,removeview,updateview,三个方法,
小结
通过一系列的跟源码与分析大致流程:ViewManager -->> WindowManager(继承自ViewManager) -->> WindowManagerImpl(继承自WindowManager) -->>WindowManagerGlobal(暴露实例给WindowManagerImpl调用处理) -->> ViewRootImpl (WindowManagerGlobal的内部具体细节实现)
接下来简单分析Activity的启动
Activity的其实很复杂的,本人也不是完全理解但是我们抓住其入口ActivityThread类即可 (android.app.ActivityThread)
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { //... ActivityClientRecord r = performResumeActivity(token, clearHide); // 这里会调用到onResume()方法 if (r != null) { final Activity a = r.activity; //... if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); // 获得window对象 View decor = r.window.getDecorView(); // 获得DecorView对象 decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); // 获得windowManager对象 WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); // 调用addView方法 } //... } }}根据注释可以看到:回调onResume方法后 内部得到windowmanager与Window并且addView,加入了一个Decorview,
总结
我们开发是基于Activity,activity的setcontentview一执行 ,系统就给与了我们一个基础的window视图PhoneWIndow实例,
PhoneWindow内部就管理了一个顶级View,DecorVew,层级如图
我们之后的操作都是建立在DecorView上来实现UI的更新。因为创建最顶级DecoView是ActivityThread的window.add进来的,同时因为ViewRoolImpl管理DecoView与window窗体之间的联系,所以DecoView任何子视图ViewRoolImpl的currentThread()其实就是ActivityThread(UI线程),当在非UI线程更新时候checkThread()就出现错误。(有点拗口但是道理就是这么个道理。)
延伸:::
1:子线程不能更新UI是错误的,严格意义上讲是(只有创建了View的顶级父视图的线程才能更新UI。其他线程不能干预,干预了ViewRootImpl就给你异常错误提示,目的是为了避免GUI多线程的其他错误)。
2:子线程如果在onResume之前更新UI(onCreate时候),此时的view只是一个对象还没有与phonewindow之间建立联系。所以可以更新。 但是onResume之后也就是View被主线程添加进window后,ViewRoolImpl此类就能管理view与window之间的联系,此时其他线程不能干预了。
- 为什么子线程不能更新UI的完全解析!!
- 子线程为什么不能更新UI
- GCD为什么不能在子线程更新UI
- 为什么不能在子线程中更新UI
- 子线程不能更新UI线程总结
- 为什么ios中,子线程为什么不能更新UI,必须回到主线程
- android 子线程真的不能更新ui吗
- Android子线程真的不能更新UI么
- Android子线程真的不能更新UI么
- 关于Android子线程不能更新UI的问题
- 子线程真的不能更新UI吗?
- 关于子线程里不能更新UI操作的解决方法
- Android子线程真的不能更新UI么
- Android 真的不能在子线程更新 UI 吗
- 子线程中真的不能更新UI?
- 子线程真的不能更新UI吗?
- 子线程一定不能更新UI吗?
- 为什么子线程不能更新Toas
- 黑马程序员——C语言---数字九宫格游戏实战练习
- IOS开发 使用XIB自定义一个UIView
- FP-Tree算法的实现
- Leetcode 之 Best Time to Buy and Sell StockI,II
- Python 的一些高级特性
- 为什么子线程不能更新UI的完全解析!!
- iOS上的http请求:get、post以及同步、异步
- SQLAlchemy使用笔记--简介
- iOS验证邮箱, 手机号, 密码, 账户名 --- 正则表达式
- C语言获取系统时间
- xcode 自己常用到的快捷键
- Add and Search Word - Data structure design
- Matlab优化问题07—fgoalattain
- GET 与POST一句话理解!