Android 视图架构详解
Activity
,DecorView
,PhoneWindow
和ViewRoot
的作用和相关关系
Android View Architecture
先来几张图,大致展现一下Android 视图架构的大概。
感谢网友提醒,泛化和实现这两种关系的箭头画反啦。以后要仔细学一遍UML了,平时经常画,如果有错误可真是闹笑话啊。
Activity和Window
总所周知,Activity并不负责视图控制,它只是控制生命周期和处理事件,真正控制视图的是Window
。一个Activity包含了一个Window,Window才是真正代表一个窗口,也就是说Activity可以没有Window,那就相当于是Service了。在ActivityThread
中也有控制Service
的相关函数或许正好印证了这一点。
Activity
和Window
的第一次邂逅是在ActivityThread
调用Activity
的attach()
函数时。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//[window]:通过PolicyManager创建window,实现callback函数,所以,当window接收到
//外界状态改变时,会调用activity的方法,
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
....
mWindow = PolicyManager.makeNewWindow(this);
//当window接收系统发送给它的IO输入事件时,例如键盘和触摸屏事件,就可以转发给相应的Activity
mWindow.setCallback(this);
.....
//设置本地窗口管理器
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
.....
}
|
在attach()
中,新建一个Window
实例作为自己的成员变量,它的类型为PhoneWindow
,这是抽象类Window
的一个子类。然后设置mWindow
的WindowManager
。
Window,Activity和DecorView
DecorView
是FrameLayout
的子类,它可以被认为是Android视图树的根节点视图。DecorView
作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout
,在这个LinearLayout里面有上下两个部分(具体情况和Android版本及主体有关),上面的是标题栏,下面的是内容栏。在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,而内容栏的id是content,在代码中可以通过ViewGroup content = (ViewGroup)findViewById(R.android.id.content)来得到content对应的layout。
Window
中有几个视图相关的比较重要的成员变量如下所示:
mDecor
:DecorView
的实例,标示Window
内部的顶级视图mContentParent
:setContetView
所设置的布局文件就加到这个视图中mContentRoot
:是DecorView
的唯一子视图,内部包含mContentParent
,标题栏和状态栏。
Activity中不仅持有一个Window
实例,还有一个类型为View
的mDecor
实例。这个实例和Window
中的mDecor
实例有什么关系呢?它又是什么时候被创建的呢?
二者其实指向同一个对象,这个对象是在Activity
调用setContentView
时创建的。我们都知道Activity
的setContentView
实际上是调用了Window
的setContentView
方法。
1
2
3
4
5
6
7
8
9
10
11
12
|
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) { //[window]如何没有DecorView,那么就新建一个
installDecor(); //[window]
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
....
//[window]第二步,将layout添加到mContentParent
mLayoutInflater.inflate(layoutResID, mContentParent);
.....
}
|
代码很清楚的显示了布局文件的视图是添加到mContentParent
中,而且Window
通过installDecor
来新建DecorView
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//[window]创建一个decorView
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); //直接new出一个DecorView返回
....
}
if (mContentParent == null) {
//[window] 这一步也是很重要的.
mContentParent = generateLayout(mDecor); //mContentParent是setContentVIew的关键啊
.....
}
....
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
.......
//[window] 根据不同的style生成不同的decorview啊
View in = mLayoutInflater.inflate(layoutResource, null);
// 加入到deco中,所以应该是其第一个child
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in; //给DecorView的第一个child是mContentView
// 这是获得所谓的content
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
}
.....
return contentParent;
}
|
从上述的代码中,我们可以清楚的看到mDecor
和mContentParent
和mContentRoot
的关系。
那么,Activity
中的mDecor
是何时被赋值的?我们如何确定它和Widnow
中的mDecor
指向同一个对象呢?我们可以查看ActivityThread
的handleResumeActivity
函数,它负责处理Activity
的resume
阶段。在这个函数中,Android直接将Window
中的DecorView
实例赋值给Activity
。
1
2
3
4
5
6
7
|
final Activity a = r.activity;
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
|
Window,DecorView 和 ViewRoot
ViewRoot
对应ViewRootImpl
类,它是连接WindowManagerService
和DecorView
的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。ViewRoot
并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent
接口,这让它可以作为View
的名义上的父视图。RootView
继承了Handler
类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。ViewRoot可以被理解为“View树的管理者”——它有一个mView成员变量,它指向的对象和上文中Window
和Activity
的mDecor
指向的对象是同一个对象。
我们来先看一下ViewRoot
的创建过程。由于ViewRoot
作为WindowMangerService
和DecorView
的纽带,只有在WindowManager
将持有DecorView
的Window
添加进窗口管理器才创建。我们可以查看WindowMangerGlobal
中的addView
函数。对WindowManager
不太熟悉的同学可以参考《Window和WindowManager解析》
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// 创建ViewRootImpl,然后将下述对象添加到列表中
....
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
....
try {
// 添加啦!!!!!!!!这是通过ViewRootImpl的setView来完成,这个View就是DecorView实例
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
....
}
....
}
|
那么,Window
是什么时候被添加到WindowManager
中的呢?我们回到ActivityThread
的handleResumeActivity
函数。我们都知道Activity的resume阶段就是要显示到屏幕上的阶段,在Activity也就是DecorView
将要显示到屏幕时,系统才会调用addView
方法。
我们在handleResumeActivity
函数中找到了下面一段代码,它调用了Activity
的makeVisible()
函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// ActivityThread
r.activity.makeVisible();
//Activity
//[windows] DecorView正式添加并显示
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
|
我们通过源代码发现,正式在makeVisible
函数中,系统进行了Window
的添加。
引用
http://wiki.jikexueyuan.com/project/deep-android-v1/surface.html
http://blog.csdn.net/guxiao1201/article/details/41744107
http://forlan.iteye.com/blog/2269381
Android 视图架构详解相关推荐
- Android视图架构详解
作者: ztelur 联系方式:segmentfault,csdn,github 转载请注明原作者.文章来源,链接,版权归原文作者所有. 最近一直在研究View的绘制相关的机制,发现需要补充一下An ...
- Android SystemUI 架构详解
Android SystemUI 架构详解 本文描述Android系统中一个核心应用SystemUI,详细赘述SystemUI中几大模块功能的实现过程.由于作者水平有限,如发现本文中错误的地方,欢迎指 ...
- Android MediaRecorder架构详解
1. 简介 在android中录制音频有两种方式,MediaRecorder和AudioRecord.两者的区别如下: (1) MediaRecorder 简单方便,不需要理会中间录制过程,结束录制后 ...
- android vold 加密,android vold架构详解(1)
首先上一张整体的结构类图 VOLD:Volume Daemon存储守护进程,用来为响应Usb/SD卡插入,拔出等动作提供服务. 系统启动时,通过解析init.rc文件来启动各种系统服务. 包括VOLD ...
- Android开发入门一之Android应用程序架构详解
Android应用程序架构详解如下: src/ java源代码存放目录 gen/自动生成目录 gen 目录中存放所有由Android开发工具自动生成的文件.目录中最重要的就是R.java文件.这个文件 ...
- DL之DeepLabv2:DeepLab v2算法的简介(论文介绍)、架构详解、案例应用等配图集合之详细攻略
DL之DeepLabv2:DeepLab v2算法的简介(论文介绍).架构详解.案例应用等配图集合之详细攻略 目录 DeepLab v2算法的简介(论文介绍) 0.实验结果 1.DeepLab-v2 ...
- Android LiveData组件详解以及LiveDataBus
转载请标明出处:https://blog.csdn.net/zhaoyanjun6/article/details/99749323 本文出自[赵彦军的博客] 一.LiveData简介 LiveDat ...
- 《Java和Android开发实战详解》——1.2节Java基础知识
本节书摘来自异步社区<Java和Android开发实战详解>一书中的第1章,第1.2节Java基础知识,作者 陈会安,更多章节内容可以访问云栖社区"异步社区"公众号查看 ...
- 《Java和Android开发实战详解》——2.2节构建Java应用程序
本节书摘来自异步社区<Java和Android开发实战详解>一书中的第2章,第2.2节构建Java应用程序,作者 陈会安,更多章节内容可以访问云栖社区"异步社区"公众号 ...
最新文章
- C语言函数集(十六)
- C语言实现聚类K-means cluster算法(附完整源码)
- nosql-redis学习 数据类型
- [Nginx]nginx 配置实例-动静分离
- html是一种描述的沙子语言,小学低年级语文阅读训练
- fork/join框架Java
- Python 万能代码模版:爬虫代码篇
- 前后端分离前端框架的主要内容是什么?
- 62个大数据可视化工具
- 数字信号处理-希尔伯特变换
- 记一次使用verdaccio 搭建本地私有npm服务器
- Matlab的一些常用功能
- 利用特性、泛型、反射生成sql操作语句(待修改
- The pgAdmin 4 server could not be contacted
- 第二章 实用工具单元
- 资金安全责任险对个人账户负责?
- 基于C++MFC的学生成绩管理系统
- 《九日集训》第四天打卡
- MoviePy - 中文文档4-MoviePy实战案例-重新构建15世纪舞蹈视频
- List、Map、Set集合的特点及常用方法
热门文章
- 区位码、国标码与机内码的转换关系
- 相干波和杨氏双缝实验(大学物理笔记)
- 计算机基础 键盘认识,计算机应用基础教程-认识键盘
- 数学实验报告1:t检验、方差分析
- yzh第十二课 工具和基础设施
- openoffice java api_OpenOffice java api UNO 教程
- SkyDrive应用介绍
- python学习笔记(二十七) -- 常用内建模块(二) Base64、MD5、SHA1、hmac
- 解决Ubuntu中安装Teamviewer成功但打开Teamviewer后无法登陆无法获取ID的问题
- java枚举类型的构造和get\set方法