Activity
,DecorView
,PhoneWindow
和ViewRoot
的作用和相关关系先来几张图,大致展现一下Android 视图架构的大概。
感谢网友提醒,泛化和实现这两种关系的箭头画反啦。以后要仔细学一遍UML了,平时经常画,如果有错误可真是闹笑话啊。
总所周知,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
。
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
;
|
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