Activity的视图层次

前言

Activity是Android中经常用到的组件,通常的用法是用startActivity方法启动一个Activity,然后在onCreate中用setContentView设置一个基本视图来显示UI。

平时我只是简单的使用这些东西来启动Activity或布局,现在我想深究一下内部这些方法内部是如何实现的。

核心代码

核心代码是ActivityThread的handleResumeActivity方法中的一块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
...
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}

这块代码不算太长,但是里面东西弯弯绕绕,接下来一行一行分析下。

获取Window对象

  • r.window = r.activity.getWindow();

r是ActivityClientRecord对象,r.activity是Activity对象,r.window是Window对象。

调用Activity的getWindow方法返回一个Window对象,接着查看这个方法的源码:

1
2
3
public Window getWindow() {
return mWindow;
}

返回Activity的私有变量mWindow,这个变量是一个抽象类Window,,这个变量在Activity的attach方法中初始化:

1
mWindow = new PhoneWindow(this);

PhoneWindow是Window的一个子类,实现了Window中的抽象方法,在这直接使用它的构造方法赋值给mWindow。

获取DevorView对象

  • View decor = r.window.getDecorView();

很显然是调用Window类的getDecorView方法返回一个View对象,查看Window中的源码:

1
public abstract View getDecorView();

由于这是个抽象函数,所以实际上调用的是PhoneWindow类中的方法,继续查看PhoneWindow的源码:

1
2
3
4
5
6
7
@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}

mDecor是PhoneWindow的一个内部类DecorView的对象。当这个变量为空时,调用installDecor初始化mDecor,否则直接返回mDecor。

继续查看installDecor方法的源码:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
...
} else {
mTitleView = (TextView)findViewById(R.id.title);
if (mTitleView != null) {
...
}
}
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
// Only inflate or create a new TransitionManager if the caller hasn't
// already set a custom one.
if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
...
}
}
}

这个方法的源码较多,但是内部都是用if语句控制,通过判断我们就能找到需要了解的东西。

初始化mDecor

mDecor肯定是由null变为初始值的,所有第一条if语句一定为true。

  • mDecor = generateDecor();

这行代码直接调用generateDecor方法生成DecorView对象,查看源码:

1
2
3
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}

可见内部实际是用DecorView的构造函数直接返回一个DecorView对象。

初始化mContentParent

同样,mContentParent也是由空变为初始值的,所以这条if语句也一定为true。

  • mContentParent = generateLayout(mDecor);

调用generateLayout方法返回一个ViewGroup给mContentParent赋值,继续看源码:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
protected ViewGroup generateLayout(DecorView decor) {
// 根据当前的Theme应用数据
TypedArray a = getWindowStyle();
// 一大串获取Theme标签值并作出反应的代码的代码
...
final Context context = getContext();
// 获取当前的Sdk版本
final int targetSdk = context.getApplicationInfo().targetSdkVersion;
// Sdk为Android 11以前的版本
final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB;
// Sdk为Android 14以前的版本
final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
// Sdk为Android 21以前的版本
final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP;
// Sdk为Android 11时需要选项菜单
final boolean targetHcNeedsOptions = context.getResources().getBoolean(
R.bool.target_honeycomb_needs_options_menu);
// 不需要工具栏
final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE);
// 根据之前的boolean值设置需不需要响应菜单键
if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) {
setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE);
} else {
setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE);
}
// 根据Theme值和相关值设置一些属性
...
// 获取窗口的布局参数
WindowManager.LayoutParams params = getAttributes();
// 更改布局参数的一些属性
...
// 当Activity不是内嵌的时候才会执行这部分,否则从container处继承相关数值。
// 只有Activity的parent不为空时才会setContainer,简单的说不看这部分也无所谓。
if (getContainer() == null) {
if (mBackgroundDrawable == null) {
if (mBackgroundResource == 0) {
mBackgroundResource = a.getResourceId(
R.styleable.Window_windowBackground, 0);
}
if (mFrameResource == 0) {
mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
}
mBackgroundFallbackResource = a.getResourceId(
R.styleable.Window_windowBackgroundFallback, 0);
// 这一块肯定不会进入
if (false) {
System.out.println("Background: "
+ Integer.toHexString(mBackgroundResource) + " Frame: "
+ Integer.toHexString(mFrameResource));
}
}
mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
}
// 设置窗口装饰,终于到了想看的地方了
int layoutResource;
int features = getLocalFeatures();
// 一大串if-else代码根据Activity的Theme来初始化layoutResource
...
// 这个方法只是简单的设置flag,字面意思就是开始变化
mDecor.startChanging();
// 根据layoutResource获取View
View in = mLayoutInflater.inflate(layoutResource, null);
// 将这个View添加到decor上,并填满decor
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
// 这个是DecorView的根内容
mContentRoot = (ViewGroup) in;
// 这个ViewGroup就是我们setContainView时添加到的父布局
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
// 如果contentParent为空就会报错
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
// 几行代码,根据feature值设置相应功能
...
// 剩下的设置——只是关于背景和标题——只应用于顶级窗口,即没有parent的Activity
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}
mDecor.setWindowBackground(background);
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
} else {
frame = null;
}
mDecor.setWindowFrame(frame);
mDecor.setElevation(mElevation);
mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
}
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
setTitleColor(mTitleColor);
}
// 这个方法只是简单的设置flag,字面意思就是结束变化
mDecor.finishChanging();
return contentParent;
}

这个方法主要是根据Window的Theme生成布局,一些代码的作用我都写在注释里了。

这个方法里重要的就是mContentParent和mContentRoot这两个视图。

mContentRoot是可能包含标题、工具条的布局,如果Theme里选择noActionBar或noTitleBar就不会有这些。

mContentParent是mContentRoot的子视图,在代码里用findViewById获取,id变量名是ID_ANDROID_CONTENT,点击跳转到定义发现即是com.android.internal.R.id.content。这个id在代码给出的几个布局里都是FrameLayout或者FrameLayout的子类的id。这个视图必须存在,不然会抛出异常。方法最后返回contentParent。

设置mDecor属性

  • mDecor.makeOptionalFitsSystemWindows();

这个方法是遍历mDecor的所有子视图,将它们的Flags都设为OPTIONAL_FITS_SYSTEM_WINDOWS,表示它们可以在适当的时候忽视适应系统窗口。

获取DecorContentParent接口

  • final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
    R.id.decor_content_parent);

DecorContentParent是一个接口类,它提供了一些关于标题或窗口的装饰方法。这里先通过id获取一个视图,再强转换成接口。

R.id.decor_content_parent是系统提供的一个XML文件screen_action_bar根布局ActionBarOverlayLayout的id,这个XML文件在之前的generateLayout方法里用到过。如果用户不使用ActionBar功能,这个接口就为空。

之所以这里选择转换成接口,是因为当我们选择自定义ActionBar时,就能针对接口编程,而不用针对实现编程。

判断decorContentParent是否为空

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
28
29
30
31
32
33
34
35
36
if (decorContentParent != null) {
// 如果decorContentParent不为空,则使用decorContentParent设置标题、
// 回调方法、icon、logo等
...
} else {
// 获取标题
mTitleView = (TextView)findViewById(R.id.title);
// 如果标题不为空
if (mTitleView != null) {
mTitleView.setLayoutDirection(mDecor.getLayoutDirection());
// 如果设置不显示标题
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
// 获取标题容器
View titleContainer = findViewById(
R.id.title_container);
// 如果容器不为空,则标题容器Visibility设为GONE
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
// 如果容器为空,则直接将标题Visibility设为GONE
} else {
mTitleView.setVisibility(View.GONE);
}
// 如果mContentParent是FrameLayout类型,
// 则将其前景设为空
if (mContentParent instanceof FrameLayout) {
((FrameLayout)mContentParent).setForeground(null);
}
// 设置标题
} else {
mTitleView.setText(mTitle);
}
}
}

当decorContentParent不为空时,使用decorContentParent实现相关设置。

设置默认背景

1
2
3
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}

mBackgroundFallbackResource是从Theme中获取的,可以自己设置,默认值为0。当mDecor的背景为空且mBackgroundFallbackResource不为0时,mDecor设置后补背景。

设置转场相关变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 如果设置存在转场功能
if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
// 直接获取Theme设定值或者直接new一个对象
if (mTransitionManager == null) {
final int transitionRes = getWindowStyle().getResourceId(
R.styleable.Window_windowContentTransitionManager,
0);
if (transitionRes != 0) {
final TransitionInflater inflater = TransitionInflater.from(getContext());
mTransitionManager = inflater.inflateTransitionManager(transitionRes,
mContentParent);
} else {
mTransitionManager = new TransitionManager();
}
}
// 根据Theme的设置初始化一些Transition类对象,以及相关变量
...
}

如果mTransitionManager为空则或去Theme中相应的设置值,如果为0则直接new一个新对象。

设定decor的Visibility

绕了一大圈,获得的最重要的信息就是mDecor内部是怎么布局的。

mDecor有一个子视图mContentRoot,而mContentRoot又有一个子视图mContentParent,而这个mContentPanrent就是用Activity的setContentView时添加视图的父节点。

现在回到核心代码:

  • decor.setVisibility(View.INVISIBLE);

setVisibility有View类实现,将decor设为INVISIBLE。

获取ViewManager接口实例

  • ViewManager wm = a.getWindowManager();

调用Activity的getWindowManager方法返回一个ViewManager实例,查看源码:

1
2
3
4
/** Retrieve the window manager for showing custom windows. */
public WindowManager getWindowManager() {
return mWindowManager;
}

直接返回Activity的一个变量mWindowManager,而这个变量在Activity的attach方法中被初始化:

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
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 = new PhoneWindow(this);
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
...
}

mWindowManager在这里是用mWindow的getWindowManager方法初始化,这个方法由Window类实现,查看源码:

1
2
3
4
5
6
7
8
9
/**
* Return the window manager allowing this Window to display its own
* windows.
*
* @return WindowManager The ViewManager.
*/
public WindowManager getWindowManager() {
return mWindowManager;
}

还是直接返回Window类的私有变量mWindowManager,而这个变量是用Window类的setWindowManager初始化的,可以看到之前出现的attach方法中,mWindow变量已经用setWindowManager初始化了,查看setWindowManager的源码:

1
2
3
4
5
6
7
8
9
10
11
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

分析wm参数具体类型

本来在这里直接查看WindowManagerImpl的createLocalWindowManager方法就行了,但是还是想深究一下wm为什么能被强制转换成WindowManagerImpl类型,免得以后忘了又要重新看代码。

参数wm即前面传入的(WindowManager)context.getSystemService(Context.Window_SERVICE),而context是attach方法的参数,这个方法在ActivityThread的performLaunchActivity中被调用:

1
2
3
4
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);

这里的appContext即传入的参数,这个参数同样是在performLaunchActivity中初始化:

1
Context appContext = createBaseContextForActivity(r, activity);

继续看createBaseContextForActivity的源码:

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
28
29
30
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
int displayId = Display.DEFAULT_DISPLAY;
try {
displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
} catch (RemoteException e) {
}
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
// For debugging purposes, if the activity's package name contains the value of
// the "debug.use-second-display" system property as a substring, then show
// its content on a secondary display if there is one.
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id));
baseContext = appContext.createDisplayContext(display);
break;
}
}
}
return baseContext;
}

里面代码还是有点多,只需要抓重点就行了。这个方法最后返回baseContext,这个变量在方法中使用appContext初始化,而appContext的类型是ContextImpl类,一个Context的子类,实现了抽象类Context中的方法。所以很明显,前面调用Context的抽象方法getSystemService就是调用ContextImpl的getSystemService方法。

查看ContextImpl源码:

1
2
3
4
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}

发现内部是调用SystemServiceRegistry的一个静态方法,继续看源码:

1
2
3
4
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}

SYSTEM_SERVICE_FETCHERS是一个哈希表,key为String类型,value是一个接口ServiceFetcher<?>,这里直接用get方法初始化fetcher。既然有get,那肯定有put。继续在SystemServiceRegistry里Ctrl+F寻找,发现了这个方法:

1
2
3
4
5
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}

第一条语句是将服务类和服务名放入一个哈希表,第二条语句则是将服务名和接口类ServiceFetcher。这个方法在SystemServiceRegistry里静态调用,注册了大量服务。根据前面传入的参数可知nama为Context.WINDOW_SERVICE。继续Ctrl+F搜一下,就找到了相应的方法:

1
2
3
4
5
6
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx.getDisplay());
}});

这里面CachedServiceFetcher实现了ServiceFetcher接口,查看它的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
private final int mCacheIndex;
public CachedServiceFetcher() {
mCacheIndex = sServiceCacheSize++;
}
@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
synchronized (cache) {
// Fetch or create the service.
Object service = cache[mCacheIndex];
if (service == null) {
service = createService(ctx);
cache[mCacheIndex] = service;
}
return (T)service;
}
}
public abstract T createService(ContextImpl ctx);
}

根据getSystemService方法、registerService传入的参数和CachedServiceFetcher的内部实现可以知道,getService返回的就是createService创建的新对象WindowManagerImpl类,这样就能理解为什么最开始WindowManager类型的wm能强制转换成WindowManagerImpl了,因为传入的参数实际就是WindowManagerImpl类,而这个类本身就实现了WindowManager接口。

初始化mWindowManager

回到正题。

mWindowManager是WindowManager类对象,而WindowManager是ViewManager的子类,所以mWindowManager可以直接当ViewManager使用。

  • mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);

这里使用WindowManagerImpl的createLocalWindowManager方法初始化mWindowManager,查看源码:

1
2
3
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}

这个方法使用wm自身的mDisplay参数和传入的Window参数创建了一个新的WindowManagerImpl对象。所以mWindowManager实际就是WindowManagerImpl的实现的接口类。这样就清楚最开始获取的ViewManager变量实际指向的是哪个类了。

获取窗口布局参数

  • WindowManager.LayoutParams l = r.window.getAttributes();

这里调用的是Window类的getAttributes方法,查看源码:

1
2
3
public final WindowManager.LayoutParams getAttributes() {
return mWindowAttributes;
}

直接返回私有变量mWindowAttributes,这个参数即当前窗口的布局参数。它在Window类内部直接被初始化:

1
2
3
4
5
6
7
8
9
10
// The current window attributes.
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();
```
### 添加视图
```java
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}

mVisibleFromClien是一个布尔值,在Activity类能通过setVisible方法修改,在performCreateCommon中也会被修改,默认值是true。

a.mWindowAdded设为true,表示Activity已经添加了Window。

接下来才是真正的添加窗口,调用的addView已经知道实际调用的是WindowManagerImpl的方法:

1
2
3
4
5
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}

这里内部继续转交给mGlobal去添加视图,mGlobal是WindowManagerGlobal类对象,查看它的addView方法:

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
28
29
30
31
32
33
34
35
36
37
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
...
ViewRootImpl root;
...
synchronized (mLock) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
...
}
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}

先将params重新转换成WindowManager.LayoutParams,之后view就是用这个布局参数。创建一个ViewRootImpl对象,然后使用setView方法,查看源码:

1
2
3
4
5
6
7
8
9
10
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
}
}
}

其他的暂不关注,这里直接用view赋值给ViewRootImpl的默认变量mView,从而使DecorView与ViewRootImpl有了联系。

总结

在前面一直说setContentView就是将View添加到mContentParent上,现在简单分析下。首先得看Activity的setContentView的源码:

1
2
3
4
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}

在里面直接调用getWindow获取PhoneWindow对象,然后调用其setContentView方法,为什么是PhoneWindow前面已经说了。PhoneWindow的setContentView源码:

1
2
3
4
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}

继续调用PhoneWindow内部重载的另一个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
...
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
...
}

在这里我们可以很明显发现一个语句mContentPanrent.addView(view, params),这里addView是用ViewGroup实现的,params的两个参数都是MATCH_PARENT,view就是我们需要添加的UI界面了。

这里分析的else分支,if分支最终应该也是这样的。当然setContetView的另一个使用资源id作参数的重载方法最终也是这样,就不分析了。

最后用图表示各个视图之间的关系: