这篇课程开头就说在"接触 Android 开发时,我始终认为它就是负责将 layout 布局中的控件渲染绘制出来的"。的确,对于layout布局怎么跟Activity关联起来的,都没有深入的去探究。而这篇课程解答了这一问题。

Activity 的 setContentView

从最初的 Activity 的 setContentView入手:

setContentView

可以看到是直接调用了Window的setContentView方法,显然 Activity 几乎什么都没做,将操作直接交给了一个 Window 来处理。getWindow 返回的是 Activity 中的全局变量 mWindow,它是 Window 窗口类型。

而这个 Window 对象的赋值则是在《startActivity 启动过程分析课程》中有讲到过:

通过反射创建 Activity 对象,并执行其 attach 方法。Window 就是在这个方法中被创建。

在 Activity 的 attach 方法中将 mWindow 赋值给一个 PhoneWindow 对象,实际上整个 Android 系统中 Window 只有一个实现类,就是 PhoneWindow。

接下来调用 setWindowManager 方法,将系统 WindowManager 传给 PhoneWindow,如下所示:

最终,在 PhoneWindow 中持有了一个 WindowManagerImpl 的引用。

PhoneWindow 的 setContentView

回到 PhoneWindow 的 setContentView:

PhoneWindow 的 setContentView

判断了mContentParent 是否为 null,不为空则调用 installDecor() 方法初始化 DecorView 和 mContentParent。然后又调用了 mLayoutInflater.inflate()方法将布局添加到 mContentParent 中。

可以看出在 PhoneWindow 中默认有一个 DecorView(实际上是一个 FrameLayout),在 DecorView 中默认自带一个 mContentParent(实际上是一个 ViewGroup)。我们自己实现的布局是被添加到 mContentParent 中的,因此经过 setContentView 之后,PhoneWindow 内部的 View 关系如下所示:

目前为止 PhoneWindow 中只是创建出了一个 DecorView,并在 DecorView 中填充了我们在 Activity 中传入的 layoutId 布局,可是 DecorView 还没有跟 Activity 建立任何联系,也没有被绘制到界面上显示。

刚接触 Android,学习生命周期时,经常会看到相关文档介绍 Activity 执行到 onCreate 时并不可见,只有执行完 onResume 之后 Activity 中的内容才是屏幕可见状态。造成这种现象的原因就是,onCreate 阶段只是初始化了 Activity 需要显示的内容,而在 onResume 阶段才会将 PhoneWindow 中的 DecorView 真正的绘制到屏幕上。

在 ActivityThread 的 handleResumeActivity 中,会调用 WindowManager 的 addView 方法将 DecorView 添加到 WMS(WindowManagerService) 上,如下所示:

WindowManger 的 addView 结果有两个:

  • DecorView 被渲染绘制到屏幕上显示;
  • DecorView 可以接收屏幕触摸事件。

WindowManager 的 addView

PhoneWindow 只是负责处理一些应用窗口通用的逻辑(设置标题栏,导航栏等)。但是真正完成把一个 View 作为窗口添加到 WMS 的过程是由 WindowManager 来完成的。

WindowManager 是接口类型,上文中我们也了解到它真正的实现者是 WindowManagerImpl 类,看一下它的 addView 方法如下:

WindowManagerImpl 也是一个空壳,它调用了 WindowManagerGlobal 的 addView 方法。

WindowMangerGlobal 是一个单例,每一个进程中只有一个实例对象。如上图红框中所示,在其 addView 方法中,创建了一个最关键的 ViewRootImpl 对象,然后通过 ViewRootImpl 的 setView 方法将 view 添加到 WMS 中。

ViewRootImpl 的 setView

  • 第一个方法可以看到调用了 requestLayout() 方法, requestLayout 是刷新布局的操作,调用此方法后 ViewRootImpl 所关联的 View 也执行 measure - layout - draw 操作,确保在 View 被添加到 Window 上显示到屏幕之前,已经完成测量和绘制操作。在自定义VIew中有时候需要重新测量和绘制的时候会调用这个方法。而这里调用了这个方法则是开始真正的将DecorView渲染绘制。
  • 第二个方法调用了mWindowSession的addToDisplay方法,这个方法将 View 添加到 WMS 中。

WindowSession 是 WindowManagerGlobal 中的单例对象,初始化代码如下:

sWindowSession 实际上是 IWindowSession 类型,是一个 Binder 类型,真正的实现类是 System 进程中的 Session。上图中红框中就是用 AIDL 获取 System 进程中 Session 的对象。

图中的 mService 就是 WMS。至此,Window 已经成功的被传递给了 WMS。剩下的工作就全部转移到系统进程中的 WMS 来完成最终的添加操作。

再看 Activity 接收触屏事件

上面提到 addView 成功有一个标志就是能够接收触屏事件,通过对 setContentView 流程的分析,可以看出添加 View 的操作实质上是 PhoneWindow 在全盘操作,背后负责人是 WMS,反之 Activity 自始至终没什么参与感。但是我们也知道当触屏事件发生之后,Touch 事件首先是被传入到 Activity,然后才被下发到布局中的 ViewGroup 或者 View。那么 Touch 事件是如何传递到 Activity 上的呢?

ViewRootImpl 中的 setView 方法中,除了调用 IWindowSession 执行跨进程添加 View 之外,还有一项重要的操作就是设置输入事件的处理:

如上图红框中所示,设置了一系列的输入通道。一个触屏事件的发生是由屏幕发起,然后经过驱动层一系列的优化计算通过 Socket 跨进程通知 Android Framework 层(实际上就是 WMS),最终屏幕的触摸事件会被发送到上图中的输入管道中。

这些输入管道实际上是一个链表结构,当某一个屏幕触摸事件到达其中的 ViewPostImeInputState 时,会经过 onProcess 来处理,如下所示:

可以看到在 onProcess 中最终调用了一个 mView的dispatchPointerEvent 方法,mView 实际上就是 之前PhoneWindow 中addView添加的 DecorView,而 dispatchPointerEvent 是被 View.java 实现的,如下所示:

最终调用了 PhoneWindow 中 Callback的dispatchTouchEvent 方法,那这个 Callback 是不是 Activity 呢?

在启动 Activity 阶段,创建 Activity 对象并调用 attach 方法时,有如下一段代码:

果然将 Activity 自身传递给了 PhoneWindow,再接着看 Activity的dispatchTouchEvent 方法:

Touch 事件在 Activity 中只是绕了一圈最后还是回到了 PhoneWindow 中的 DecorView 来处理。剩下的就是从 DecorView 开始将事件层层传递给内部的子 View 中了:

也就是从这张图可以理解顺序为:
ViewPostImeInputStage.onProcess -> ViewPostImeInputStage.processPointerEvent -> View.dispatchPointerEvent -> PhoneWindow$DecorView.dispatchTouchEvent -> 内部子View

例如下面log:

at com.example.helloworld.MainActivity.dispatchTouchEvent(MainActivity.java:103)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2359)
at android.view.View.dispatchPointerEvent(View.java:8698)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4530)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4388)

总结

这节课主要通过 setContentView 的流程,分析了 Activity、Window、View 之间的关系。整个过程 Activity 表面上参与度比较低,大部分 View 的添加操作都被封装到 Window 中实现。而 Activity 就相当于 Android 提供给开发人员的一个管理类,通过它能够更简单的实现 Window 和 View 的操作逻辑。

最后再简单列一下整个流程需要注意的点:

  • 一个 Activity 中有一个 window,也就是 PhoneWindow 对象,在 PhoneWindow 中有一个 DecorView,在 setContentView 中会将 layout 填充到此 DecorView 中。
  • 一个应用进程中只有一个 WindowManagerGlobal 对象,因为在 ViewRootImpl 中它是 static 静态类型。
  • 每一个 PhoneWindow 对应一个 ViewRootImple 对象。
  • WindowMangerGlobal 通过调用 ViewRootImpl 的 setView 方法,完成 window 的添加过程。
  • ViewRootImpl 的 setView 方法中主要完成两件事情:View 渲染(requestLayout)以及接收触屏事件。

深入理解WMS(三):剖析Activity,View,Window之间的关系相关推荐

  1. 【Android 界面效果10】Android中View,ViewGroup,Window之间的关系

    一.首先说说View和ViewGroup吧 Android系统中的所有UI类都是建立在View和ViewGroup这两个类的基础上的.所有View的子类成为"Widget",所有V ...

  2. 深入理解WMS(一):Window的创建过程

    8.3 Window的创建过程 View是Android中的视图的呈现方式,但是View不能单独存在,它必须附着在Window这个抽象的概念上面,因此有视图的地方就有Window.Android中可以 ...

  3. 理清Activity、View及Window之间关系

    我的简书同步发布:理清Activity.View及Window之间关系 转载请注明出处:[huachao1001的专栏:http://blog.csdn.net/huachao1001] View.W ...

  4. 从中国封建历史的发展来理解云计算、雾计算、边缘计算以及云原生之间的关系

    前言 互联网的快速发展,带来了一大批新的名词,这次名词的更新换代的速度也是快的惊人,往往一波未平一波又起,使得大家不能墨守成规,必须不断学习才能赶得上科技和技术的发展潮流. 计算机行业更是如此,可能真 ...

  5. android 如何获得activity的view对象,Android的Activity 、 Window 、 View之间的关系

    什么是Activity .View . Window? Activity:是Android 四大组件之一, 是存放View对象的容器,也是我们界面的载体,可以用来展示一个界面.它有一个SetConte ...

  6. 文化袁探索专栏——Activity、Window和View三者间关系

    文化袁探索专栏--Activity.Window和View三者间关系 <文化袁探索专栏--View三大流程#Measure 文化袁探索专栏--View三大流程#Layout 文化袁探索专栏--H ...

  7. WMS(二):Window的删除过程

    作者:刘望舒 链接:https://www.jianshu.com/p/f9b3787fc0cd 前言 在本系列文章中,我提到过:Window的操作分为两大部分,一部分是WindowManager处理 ...

  8. 深入理解WMS(二):Dialog与Toast源码解析

    作者:ScottStone 链接:https://www.jianshu.com/p/1090d6c33dec 通过上面的分析可以看出,View是Android中的视图呈现方式,但是View并不能单独 ...

  9. Android解析WindowManagerService(二)WMS的重要成员和Window的添加过程

    前言 在本系列的上一篇文章中,我们学习了WMS的诞生,WMS被创建后,它的重要的成员有哪些?Window添加过程的WMS部分做了什么呢?这篇文章会给你解答. 1.WMS的重要成员 所谓WMS的重要成员 ...

最新文章

  1. 可以查python题的_python练习题 -股票查询
  2. 中国科学院院士骆清铭: “看见”大脑
  3. ssl单向tomcat配置webservice访问方法
  4. node.js项目中常量的配置 - 个人文章 - SegmentFault 思否
  5. 教你打开线程、进程和协程的大门!
  6. 【博客管理】短期长期计划【置顶】
  7. VirtualBox是什么
  8. Atitit 粘贴路径参数法 跨进程通讯法 目录 1. .IPC(Inter-Process Communication,跨进程通信) 1 1.1. .IPC的使用场景: 2 2. 传统的进程间通
  9. 重锤痛击 robocode!
  10. 【C/C++】LibVLC库在逐帧提取的开发中回调设置帧格式
  11. 世嘉MD游戏开发【十三】:音乐和音效
  12. Acrel-7000企业能源管控平台助力新疆某企业实现双碳双控
  13. tomcat部署静态html网站方法
  14. 消防设施操作员考试真题、模拟练习题库(7)
  15. 全新视角!带你一文读懂ChatGPT!
  16. 揭开全景相机的创业真相
  17. 计算机图形图像处理专业知识,计算机图形图像处理专业技术的突破研究
  18. 基于微信在线考试小程序系统设计与实现 开题报告
  19. html打造动画【系列4】- 哆啦a梦
  20. 这个视频「橡皮擦」让你瞬间消失,头发丝都不留 | ECCV 2020

热门文章

  1. mysql建帐号数据库出现反斜线_[MySQL FAQ]系列 -- 账号密码包含反斜线时怎么办
  2. barplot参数 python_Python零基础入门Python数据分析最好的实战项目
  3. 若个人计算机主板上的内存条松动,电脑内存条松动后故障现象及解决方法
  4. arraylist从大到小排序_初学Python最简易入门之十四排序算法10对字典排序
  5. 微信小程序中WebView中原生组件限制问题解析
  6. webgl之3d动画
  7. 直播协议HLS技术要点分析:分段生成与m2u8文件
  8. UIScrollView无法滚动可能的原因及解决办法分析
  9. DNS原理及其解析过程
  10. 格式资料python sqlalchemy 查询结果转化为 Json格式