0%

Activity显示到界面流程

之前分析过Activity的启动流程,虽然Activity启动了,但是显示在界面上的并不是Activity,接下来将分析Activity显示到界面的流程。

首先看看各个相关类的关系图
Window初始化
View的生成过程

1.Activity.attach

在Activity启动流程的35步中,我们可以看到Activity的实例化,然后调用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
26
27
28
29
30
31
32
33
34
35
36
37
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,
Object lastNonConfigurationInstance,
HashMap<String,Object> lastNonConfigurationChildInstances,
Configuration config) {
attachBaseContext(context);

mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
mUiThread = Thread.currentThread();

mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstance = lastNonConfigurationInstance;
mLastNonConfigurationChildInstances = lastNonConfigurationChildInstances;

mWindow.setWindowManager(null, mToken, mComponent.flattenToString());
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}

里面实际就是初始化一些值,新建了一个window对象。

2. PolicyManager.makeNewWindow

1
2
3
4
5
6
7
8
9
10
11
12
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}

public PhoneWindow makeNewWindow(Context context) {
return new PhoneWindow(context);
}

public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}

3. PhoneWindow.setCallback

这个很重要,后面接受键盘和触屏时间都是回调 Window.CallBack,所以Activity能接受到键盘和触屏事件

4. Window.setWindowManager

1
2
3
4
5
6
7
8
9
public void setWindowManager(WindowManager wm,
IBinder appToken, String appName) {
mAppToken = appToken;
mAppName = appName;
if (wm == null) {
wm = WindowManagerImpl.getDefault();
}
mWindowManager = new LocalWindowManager(wm);
}

5. WindowManagerImpl.getDefault

1
2
3
4
5
6
private static WindowManagerImpl mWindowManager = new WindowManagerImpl();  

public static WindowManagerImpl getDefault()
{
return mWindowManager;
}

6. new LocalWindowManager(wm)

1
2
3
4
5
6
7
8
9
10
11
12
13
private class LocalWindowManager implements WindowManager {  
LocalWindowManager(WindowManager wm) {
mWindowManager = wm;
mDefaultDisplay = mContext.getResources().getDefaultDisplay(
mWindowManager.getDefaultDisplay());
}

......

private final WindowManager mWindowManager;

private final Display mDefaultDisplay;
}

以上就是Activity中Window的创建和初始化过程。

7. Activity.setContentView

1
2
3
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
}

8. PhoneWindow.setContentView

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}

9. PhoneWindow.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
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);

mTitleView = (TextView)findViewById(com.android.internal.R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
View titleContainer = findViewById(com.android.internal.R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
if (mContentParent instanceof FrameLayout) {
((FrameLayout)mContentParent).setForeground(null);
}
} else {
mTitleView.setText(mTitle);
}
}
}
}

10. PhoneWindow.generateDecor

1
2
3
4
5
6
7
8
9
10
11
12
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
}

}

这里就是新建一个DecorView,DecorView是继承FrameLayout,是根View。

11. PhoneWindow.generateLayout

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.

TypedArray a = getWindowStyle();

if (false) {
System.out.println("From style:");
String s = "Attrs:";
for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
+ a.getString(i);
}
System.out.println(s);
}

mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}

if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
}

if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));
}

if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));
}

WindowManager.LayoutParams params = getAttributes();

if (!hasSoftInputMode()) {
params.softInputMode = a.getInt(
com.android.internal.R.styleable.Window_windowSoftInputMode,
params.softInputMode);
}

if (a.getBoolean(com.android.internal.R.styleable.Window_backgroundDimEnabled,
mIsFloating)) {
/* All dialogs should have the window dimmed */
if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) {
params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
}
params.dimAmount = a.getFloat(
android.R.styleable.Window_backgroundDimAmount, 0.5f);
}

if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
}

// The rest are only done if this window is not embedded; otherwise,
// the values are inherited from our container.
if (getContainer() == null) {
if (mBackgroundDrawable == null) {
if (mBackgroundResource == 0) {
mBackgroundResource = a.getResourceId(
com.android.internal.R.styleable.Window_windowBackground, 0);
}
if (mFrameResource == 0) {
mFrameResource = a.getResourceId(com.android.internal.R.styleable.Window_windowFrame, 0);
}
if (false) {
System.out.println("Background: "
+ Integer.toHexString(mBackgroundResource) + " Frame: "
+ Integer.toHexString(mFrameResource));
}
}
mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
}

// Inflate the window decor.

int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_title_icons;
} else {
layoutResource = com.android.internal.R.layout.screen_title_icons;
}
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0) {
layoutResource = com.android.internal.R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_custom_title;
} else {
layoutResource = com.android.internal.R.layout.screen_custom_title;
}
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
layoutResource = com.android.internal.R.layout.dialog_title;
} else {
layoutResource = com.android.internal.R.layout.screen_title;
}
// System.out.println("Title!");
} else {
// Embedded, so no decoration is needed.
layoutResource = com.android.internal.R.layout.screen_simple;
// System.out.println("Simple!");
}

mDecor.startChanging();

View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}

if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}

// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
Drawable drawable = mBackgroundDrawable;
if (mBackgroundResource != 0) {
drawable = getContext().getResources().getDrawable(mBackgroundResource);
}
mDecor.setWindowBackground(drawable);
drawable = null;
if (mFrameResource != 0) {
drawable = getContext().getResources().getDrawable(mFrameResource);
}
mDecor.setWindowFrame(drawable);

// System.out.println("Text=" + Integer.toHexString(mTextColor) +
// " Sel=" + Integer.toHexString(mTextSelectedColor) +
// " Title=" + Integer.toHexString(mTitleColor));

if (mTitleColor == 0) {
mTitleColor = mTextColor;
}

if (mTitle != null) {
setTitle(mTitle);
}
setTitleColor(mTitleColor);
}

mDecor.finishChanging();

return contentParent;
}

虽然这里的代码很多,其实就是设置Activity主题、ActionBar等内容,然后将显示content的ViewGroup返回。

12. ActivityThread.handleResumeActivity

在Activity启动流程的34步,在Activity 初始化和create,调用handleResumeActivity

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
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();

ActivityClientRecord r = performResumeActivity(token, clearHide);

if (r != null) {
final Activity a = r.activity;

final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
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();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}

} else {

}
}

这里的getWindowManager就是第六步设置的LocalWindowManager

13. LocalWindowManager.addView

1
2
3
4
public final void addView(View view, ViewGroup.LayoutParams params) {
....
mWindowManager.addView(view, params);
}

14. WindowManagerImpl.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
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
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
{
if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);

if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException(
"Params must be WindowManager.LayoutParams");
}

final WindowManager.LayoutParams wparams
= (WindowManager.LayoutParams)params;

ViewRoot root;
View panelParentView = null;

synchronized (this) {
// Here's an odd/questionable case: if someone tries to add a
// view multiple times, then we simply bump up a nesting count
// and they need to remove the view the corresponding number of
// times to have it actually removed from the window manager.
// This is useful specifically for the notification manager,
// which can continually add/remove the same view as a
// notification gets updated.
int index = findViewLocked(view, false);
if (index >= 0) {
if (!nest) {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
root = mRoots[index];
root.mAddNesting++;
// Update layout parameters.
view.setLayoutParams(wparams);
root.setLayoutParams(wparams, true);
return;
}

// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews != null ? mViews.length : 0;
for (int i=0; i<count; i++) {
if (mRoots[i].mWindow.asBinder() == wparams.token) {
panelParentView = mViews[i];
}
}
}

root = new ViewRoot(view.getContext());
root.mAddNesting = 1;

view.setLayoutParams(wparams);

if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRoot[1];
mParams = new WindowManager.LayoutParams[1];
} else {
index = mViews.length + 1;
Object[] old = mViews;
mViews = new View[index];
System.arraycopy(old, 0, mViews, 0, index-1);
old = mRoots;
mRoots = new ViewRoot[index];
System.arraycopy(old, 0, mRoots, 0, index-1);
old = mParams;
mParams = new WindowManager.LayoutParams[index];
System.arraycopy(old, 0, mParams, 0, index-1);
}
index--;

mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
}
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
}

WindowManagerImpl类有三个成员变量mViews、mRoots和mParams,它们分别是类型为View、ViewRoot和WindowManager.LayoutParams的数组。这三个数组的大小是始终保持相等的。
这样, 有关联关系的View对象、ViewRoot对象和WindowManager.LayoutParams对象就会分别保存在数组mViews、mRoots和mParams的相同位置上,也就是说,
mViews[i]、mRoots[i]和mParams[i]所描述的View对象、ViewRoot对象和WindowManager.LayoutParams对象是具有关联关系的。因此,WindowManagerImpl类的三个参数版本的成员函数addView在关联一个View对象、
一个ViewRoot对象和一个WindowManager.LayoutParams对象的时候,只要分别将它们放在数组mViews、mRoots和mParams的相同位置上就可以了。

15. ViewRoot.setView

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
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
attrs = mWindowAttributes;
if (view instanceof RootViewSurfaceTaker) {
mSurfaceHolderCallback =
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
}
}
Resources resources = mView.getContext().getResources();
CompatibilityInfo compatibilityInfo = resources.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();

boolean restore = false;
if (mTranslator != null) {
restore = true;
attrs.backup();
mTranslator.translateWindowLayout(attrs);
}
if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);

if (!compatibilityInfo.supportsScreen()) {
attrs.flags |= WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW;
}

mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
mAttachInfo.mScalingRequired = mTranslator != null;
mAttachInfo.mApplicationScale =
mTranslator == null ? 1.0f : mTranslator.applicationScale;
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
}
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */

// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
mInputChannel = new InputChannel();
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {

} finally {
if (restore) {
attrs.restore();
}
}
}
}
}

这里会请求Layout计算视图的大小,然后通过sWindowSession通知WindowManagerService添加视图,sWindowSession是Binder对象。

16. 总结

首先是Activity的初始化会创建一个Window对象,初始化window对象中的数据,然后调用生命周期onCreate方法,然后设置ContentView,其实调用的是Window中的ContentView,Window对象中会新建DecorView(根布局),
然后将自定义的View添加到DecorView中,然后ActivityThread调用到handleResumeActivity,将DecorView添加到ViewRoot中,ViewRoot中含有WindowManagerService的远程Binder对象,然后调用WindowManagerService
将DecorView显示在界面中。