Android中Activity、Window、ViewRootImpl与子线程更新UI
三者层级关系
1、Window
- Window是一个抽象类,唯一的实现类是PhoneWindow
- Window分为三种类型应用Window、子Window、系统Window。子Window无法独立存在必须依赖父级Window,例如Dialog必须依附于Activity
- Window分层,在显示时层级高的窗口会覆盖在在层级低的窗口
类型 | 层级(z-ordered) | 例子 |
---|---|---|
应用 Window | 1~99 | Activity |
子 Window | 1000~1999 | Dialog |
系统 Window | 2000~2999 | Toast |
2、WindowManager
- WindowManager是Window的管理者,WindowManager是一个接口它的实现类是WindowMangerImpl
- WindowManager只提供是对View的add、update、remove操作
- WindowManager的addView()方法最终会创建新的ViewRootImpl而addContentView()直接把View添加到DecorView上
3、WindowManagerGlobal
WindowManagerGlobal是单例模式,进程唯一
WindowManager的add、update、remove操作最终会调用WindowManagerGlobal的方法
WindowManagerGlobal下管理了4个List
ArrayList<View> mViews; //——所有Window的根View
ArrayList<ViewRootImpl> mRoots; //所有根View对应的ViewRootImpl
ArrayList<WindowManager.LayoutParams> mParams; //所有根View(Window)对应的布局参数
ArraySet<View> mDyingViews; //待销毁的根View(Window)
- WindowManagerGlobal负责对ViewRootImpl进行统一管理,而具体实现是在ViewRootImpl中
4、ViewRootImpl
关系图
- 一个Window(PhoneWindow)可能存在多个ViewRootImpl链,所以需要一个Window将所有链管理起来
- WindowManager.addView()调用WindowManagerGlobal.addView(),最终在WindowManagerGlobal.addView()方法中创建RootViewImpl
//WindowManagerGlobal.addView()public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow, int userId) {//检查参数是否合法if (view == null) {throw new IllegalArgumentException("view must not be null");}if (display == null) {throw new IllegalArgumentException("display must not be null");}if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");}//子Window需要调整部分布局参数final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;if (parentWindow != null) {parentWindow.adjustLayoutParamsForSubWindow(wparams);} else {final Context context = view.getContext();if (context != null&& (context.getApplicationInfo().flags& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;}}ViewRootImpl root;View panelParentView = null;synchronized (mLock) {...//创建ViewRootImpl对象root = new ViewRootImpl(view.getContext(), display);//设置View的布局属性view.setLayoutParams(wparams);//将相关信息保存到对应集合mViews.add(view);mRoots.add(root);mParams.add(wparams);try {root.setView(view, wparams, panelParentView, userId);//调用ViewRootImpl对象的setView方法(这里也是View绘制的根源)} catch (RuntimeException e) {...}}
}
- 一个Window可能调用多次WindowManager.addView(),所以可能有多个ViewRootImpl,例如调用3次WindowManager.addView()就存在1个Activity创建时新建的ViewRootImpl和3个后续创建的ViewRootImpl共4个
- ViewRootImpl是链接WindowManager和DecorView的纽带。ViewRootImpl有很多作用,它负责Window中对View的操作,是View的绘制流程和事件分发的发起者
- ViewRootImpl负责与WMS通信
5、WindowManagerService(WMS)
- WMS负责实际的窗口绘制工作
- ViewRootImpl中的mWindowSession参数是IWindowSession的实例,用于WMS通讯的代理
public ViewRootImpl(Context context, Display display, IWindowSession session,boolean useSfChoreographer) {mContext = context;mWindowSession = session;//从WindowManagerGlobal中传递过来的IWindowSession的实例,它是ViewRootImpl和WMS进行通信的代理。mDisplay = display;mThread = Thread.currentThread();//保存当前线程mFirst = true; //true表示第一次添加视图mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,context);...
}
子线程更新UI
我们一般在主线程更新UI,如果将更新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:6581)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:924)
……
通过错误信息可以看到问题出在ViewRootImpl.checkThread()方法
void checkThread() {if (mThread != Thread.currentThread()) {throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");}
}
可以看出在更新UI时会将当前线程与mThread做对比,不相同则报错,我们顺着mThread看看它是怎么来的
public ViewRootImpl(Context context, Display display, IWindowSession session,boolean useSfChoreographer){...mThread = Thread.currentThread();...}
mThread是在ViewRootImpl初始化时记录的调用初始化的线程,也就是说再更新UI时并没有判断是否是主线程,而是判断更新UI的线程与创建UI的线程是否一致。
Activity的ViewRootImpl是在Activity.onResume()中创建的,而Activity的创建必须在主线程中,所以一般情况下只能在主线程更新UI
顺着这个思路想,我们完全可以在子线程中更新UI,分为两种情况
- 在ViewRootImpl创建之前,也就是在onResume之前子线程中更新UI
- 将ViewRootImpl初始化过程放在子线程,这样我们也能在这个子线程中更新UI
第一种方法可以在onCreat中更新UI,但由于线程的不确定性运行起来不稳定
我们可以利用第二种方法,将ViewRootImpl初始化过程放在HandlerThread中
//子线程创建ViewRootImpl
Runnable runnable = new Runnable() {@Overridepublic void run() {TextView tv = new TextView(context);tv.setBackgroundColor(Color.GRAY); //背景灰色tv.setGravity(Gravity.CENTER); //居中展示tv.setTextSize(30);WindowManager manager = context.getWindowManager();WindowManager.LayoutParams params = new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT,0,0,WindowManager.LayoutParams.FIRST_SUB_WINDOW,WindowManager.LayoutParams.TYPE_TOAST,PixelFormat.TRANSPARENT);//最终调用WindowManagerGloble.addView中创建ViewRootImplmanager.addView(tv, params);}};//子线程中每隔1秒更新UI
Runnable runnable2 = new Runnable() {@Overridepublic void run() {textView.setText(String.valueOf(time));Log.d("textViewThread : ", String.valueOf(Thread.currentThread()));time++;handler.postDelayed(this, 1000);}};thread = new HandlerThread(THREAD_ID);
thread.start();
handler = new Handler(thread.getLooper());handler.post(runnable);
handler.post(runnable2);
通过上面在子线程中创建ViewRootImpl的方式,可以在子线程中自由的更新UI不用担心报错
参考文章
Window和WindowManager和ViewRootImpl 作者:zhan_haoyu
Activity、Window、ViewRoot、DecorView的关系 作者:元浩875
Android WMS(一)-窗口管理 作者:stan_Z
Android中Activity、Window、ViewRootImpl与子线程更新UI相关推荐
- android 关于关于子线程更新UI的一些事
我们在看一些书或者博客时总是会看到一句话"android更新UI操作都是在Main主线程中,子线程中不能进行UI更新操作"那么,在子线程中真的不能进行UI的更新操作吗? //源码环 ...
- Android子线程更新UI的方法总结
消息机制,对于Android开发者来说,应该是非常熟悉.对于处理有着大量交互的场景,采用消息机制,是再好不过了.有些特殊的场景,比如我们都知道,在Android开发中,子线程不能更新UI,而主线程又不 ...
- Android为什么不能在子线程更新UI
Android为什么不能在子线程更新UI Android为什么不能在子线程更新UI? 如果不做这个校验,是不是我也可以正常在子线程更新UI 但是google为什么要这样去设计呢 ViewRootImp ...
- 面试官问我:Andriod为什么不能在子线程更新UI?
记得看文章三部曲,点赞,评论,转发. 微信搜索[程序员小安]关注还在移动开发领域苟活的大龄程序员,"面试系列"文章将在公众号同步发布. 1.前言 看完<你为什么在现在的公司不 ...
- AndroidStudio子线程更新UI的几种方式
在安卓开发中,大部分情况下是不能在子线程直接更新UI的,只能在UI线程更新UI,其根本原因在于加入在一个Activity中有多个线程去更新UI,且没有加锁机制,可能会产生界面混乱的情况,但是如果都加锁 ...
- C#利用Invoke和委托实现子线程更新UI(方式1)
UI布局如下 委托定义如下: public delegate void SetMessageDelegate(string message); From1的代码如下: public partial c ...
- android类之间的关系,Android 中Activity,Window和View之间的关系
Activity是Android应用程序的载体,允许用户在其上创建一个用户界面,并提供用户处理事件的API,如 onKeyEvent, onTouchEvent等. 并维护应用程序的生命周期.Acti ...
- 子线程更新UI,牵扯activity的启动过程
http://m.blog.csdn.net/article/details?id=43449123 点击打开链接
- pyqt5 子线程更新ui
from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * import time''' 信号 ...
最新文章
- 树形dp技巧,多叉树转二叉树
- 观察者模式及Java实现例子
- 有外键约束的子表插入数据时出现的错误
- 20051020:该办宽带了
- c51语言的设计步骤,第3章节单片机c51语言程序的设计基本.ppt
- python的socket模块_python模块:socket模块
- Spring自学日志01
- Conversion of Continuous-Valued Deep Networks to Efficient Event-Driven Networks for Image Classific
- 基于ZStack构建物联网平台
- 联想拯救者 Legion Y7000P 安装 Ubuntu 18.04.2 LTS amd64 遇到的问题解决
- Juniper-SRX-基于域控认证的用户防火墙
- eltable 无数据文案修改_写文案不断打磨修改,让你的文案简单易懂
- 如何在Word文档中粘贴有行号的代码
- 用HTML写一首诗并配上图片,需要满足诗的格式
- Android apps浅析01-Amazed:一个简单但令人上瘾的加速度为基础的大理石指导游戏。...
- 11 项目的工程文件存在哪里
- Jackson转换JSON
- Unity使用Shader实现3D模型外描边效果ObjectOutline.shader
- 浅析领导力和执行力在企业管理中的运用
- 信息安全第7章 网络安全