三者层级关系

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,分为两种情况

  1. 在ViewRootImpl创建之前,也就是在onResume之前子线程中更新UI
  2. 将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相关推荐

  1. android 关于关于子线程更新UI的一些事

    我们在看一些书或者博客时总是会看到一句话"android更新UI操作都是在Main主线程中,子线程中不能进行UI更新操作"那么,在子线程中真的不能进行UI的更新操作吗? //源码环 ...

  2. Android子线程更新UI的方法总结

    消息机制,对于Android开发者来说,应该是非常熟悉.对于处理有着大量交互的场景,采用消息机制,是再好不过了.有些特殊的场景,比如我们都知道,在Android开发中,子线程不能更新UI,而主线程又不 ...

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

    Android为什么不能在子线程更新UI Android为什么不能在子线程更新UI? 如果不做这个校验,是不是我也可以正常在子线程更新UI 但是google为什么要这样去设计呢 ViewRootImp ...

  4. 面试官问我:Andriod为什么不能在子线程更新UI?

    记得看文章三部曲,点赞,评论,转发. 微信搜索[程序员小安]关注还在移动开发领域苟活的大龄程序员,"面试系列"文章将在公众号同步发布. 1.前言 看完<你为什么在现在的公司不 ...

  5. AndroidStudio子线程更新UI的几种方式

    在安卓开发中,大部分情况下是不能在子线程直接更新UI的,只能在UI线程更新UI,其根本原因在于加入在一个Activity中有多个线程去更新UI,且没有加锁机制,可能会产生界面混乱的情况,但是如果都加锁 ...

  6. C#利用Invoke和委托实现子线程更新UI(方式1)

    UI布局如下 委托定义如下: public delegate void SetMessageDelegate(string message); From1的代码如下: public partial c ...

  7. android类之间的关系,Android 中Activity,Window和View之间的关系

    Activity是Android应用程序的载体,允许用户在其上创建一个用户界面,并提供用户处理事件的API,如 onKeyEvent, onTouchEvent等. 并维护应用程序的生命周期.Acti ...

  8. 子线程更新UI,牵扯activity的启动过程

    http://m.blog.csdn.net/article/details?id=43449123 点击打开链接

  9. pyqt5 子线程更新ui

    from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * import time''' 信号 ...

最新文章

  1. 树形dp技巧,多叉树转二叉树
  2. 观察者模式及Java实现例子
  3. 有外键约束的子表插入数据时出现的错误
  4. 20051020:该办宽带了
  5. c51语言的设计步骤,第3章节单片机c51语言程序的设计基本.ppt
  6. python的socket模块_python模块:socket模块
  7. Spring自学日志01
  8. Conversion of Continuous-Valued Deep Networks to Efficient Event-Driven Networks for Image Classific
  9. 基于ZStack构建物联网平台
  10. 联想拯救者 Legion Y7000P 安装 Ubuntu 18.04.2 LTS amd64 遇到的问题解决
  11. Juniper-SRX-基于域控认证的用户防火墙
  12. eltable 无数据文案修改_写文案不断打磨修改,让你的文案简单易懂
  13. 如何在Word文档中粘贴有行号的代码
  14. 用HTML写一首诗并配上图片,需要满足诗的格式
  15. Android apps浅析01-Amazed:一个简单但令人上瘾的加速度为基础的大理石指导游戏。...
  16. 11 项目的工程文件存在哪里
  17. Jackson转换JSON
  18. Unity使用Shader实现3D模型外描边效果ObjectOutline.shader
  19. 浅析领导力和执行力在企业管理中的运用
  20. 信息安全第7章 网络安全

热门文章

  1. 2022机修钳工(高级)考试题库模拟考试平台操作
  2. vue3 setup 语法糖的项目实战用法
  3. 汇编文件大写.S后缀和小写.s后缀的区别
  4. Android OpenGL添加光照和材料属性
  5. Vivado使用:综合篇(三)综合属性
  6. oracle11g r2 64 补丁,win10系统下oracle11g R2的64位版本安装教程
  7. 基于xwiki部署企业内部知识管理平台
  8. 任志强称地产投资增速将反弹 不借救市哄抬房价
  9. 回溯法求解N皇后问题
  10. 整理不易的软件测试学习线路