每一个Activity组件都有一个关联的Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含有一个View对象,用来描述应用程序窗口的视图。应用程序窗口视图是真正用来实现UI内容和布局的,也就是说,每一个Activity组件的UI内容和布局都是通过与其所关联的一个Window对象的内部的一个View对象来实现的。在本文中,我们就详细分析应用程序窗口视图的创建过程。
应用程序窗口内部所包含的视图对象的实际类型为DecorView。DecorView类继承了View类,是作为容器(ViewGroup)来使用的,它的实现如图
每一个Activity对象都有一个关联的ViewRootImpl对象,相当于是MVC模型中的Controller,它有以下职责:
1. 负责为应用程序窗口视图创建Surface。
2. 配合WindowManagerService来管理系统的应用程序窗口。
3. 负责管理、布局和渲染应用程序窗口视图的UI。
从前面Android应用程序启动过程源代码分析一文可以知道,Activity组件在启动的过程中,会调用ActivityThread类的成员函数handleLaunchActivity,用来创建以及首次激活Activity组件,因此,接下来我们就从这个函数开始,具体分析应用程序窗口的视图对象及其所关联的ViewRootImpl对象的创建过程,如图所示
setContentView中创建DecorView对象
一般在Activity的子类的onCreate方法中都会实现setContentView函数,我们来看Activity的这个函数:
-
public void setContentView(View view) {
-
getWindow().setContentView(view);
-
initWindowDecorActionBar();
-
}
调用了PhoneWindow的setContentView函数,而在这个函数中调用了installDecor函数来创建DecorView对象
-
@
Override
-
public
void
setContentView
(View view, ViewGroup.LayoutParams params) {
-
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
-
// decor, when theme attributes and the like are crystalized. Do not check the feature
-
// before this happens.
-
if (mContentParent == null) {
-
installDecor();
-
}
else
if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
-
mContentParent.removeAllViews();
-
}
-
-
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
-
view.setLayoutParams(params);
-
final Scene newScene =
new Scene(mContentParent, view);
-
transitionTo(newScene);
-
}
else {
-
mContentParent.addView(view, params);
-
}
-
mContentParent.requestApplyInsets();
-
final Callback cb = getCallback();
-
if (cb != null && !isDestroyed()) {
-
cb.onContentChanged();
-
}
-
}
在installDecor函数中调用了generateDecor函数来创建DecorView
-
private void installDecor() {
-
if (mDecor == null) {
-
mDecor = generateDecor();
-
......
-
protected DecorView generateDecor() {
-
return
new DecorView(getContext(),
-1);
-
}
创建ViewRootImpl对象
下面我们再从ActivityThread的handleResumeActivity函数看,先调用了performResumeActivity函数来查找这个Activity,后面主要调用了WindowManager的addView函数。
-
final void handleResumeActivity(IBinder token,
-
boolean clearHide, boolean isForward, boolean reallyResume) {
-
// If we are getting ready to gc after going to the background, well
-
// we are back active so skip it.
-
unscheduleGcIdler();
-
mSomeActivitiesChanged =
true;
-
-
// TODO Push resumeArgs into the activity for consideration
-
ActivityClientRecord r = performResumeActivity(token, clearHide);
-
-
if (r != null) {
-
final Activity a = r.activity;
-
-
if (localLOGV) Slog.v(
-
TAG,
"Resume " + r +
" started activity: " +
-
a.mStartedActivity +
", hideForNow: " + r.hideForNow
-
+
", finished: " + a.mFinished);
-
-
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);
-
}
我们先来看performResumeActivity函数,这个函数主要是根据token来寻找ActivityClientRecord,然后调用了Activity的performResume方法。
-
public final ActivityClientRecord performResumeActivity(IBinder token,
-
boolean clearHide) {
-
ActivityClientRecord r = mActivities.get(token);
-
if (localLOGV) Slog.v(TAG,
"Performing resume of " + r
-
+
" finished=" + r.activity.mFinished);
-
if (r != null && !r.activity.mFinished) {
-
if (clearHide) {
-
r.hideForNow =
false;
-
r.activity.mStartedActivity =
false;
-
}
-
try {
-
r.activity.onStateNotSaved();
-
r.activity.mFragments.noteStateNotSaved();
-
if (r.pendingIntents != null) {
-
deliverNewIntents(r, r.pendingIntents);
-
r.pendingIntents = null;
-
}
-
if (r.pendingResults != null) {
-
deliverResults(r, r.pendingResults);
-
r.pendingResults = null;
-
}
-
r.activity.performResume();
-
-
EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED,
-
UserHandle.myUserId(), r.activity.getComponentName().getClassName());
-
-
r.paused =
false;
-
r.stopped =
false;
-
r.state = null;
-
r.persistentState = null;
-
}
catch (Exception e) {
-
if (!mInstrumentation.onException(r.activity, e)) {
-
throw
new RuntimeException(
-
"Unable to resume activity "
-
+ r.intent.getComponent().toShortString()
-
+
": " + e.toString(), e);
-
}
-
}
-
}
-
return r;
-
}
后面有调用了Activity的getWindowManager方法获取WindowManager,之前的博客有分析过,这个WindowManager就是WindowManagerImpl对象。下面也就是调用了WindowManagerImpl的addView函数。
我们来看WindowManagerImpl的addView函数,其实就是调用了WindowManagerGlobal的addView函数
-
@
Override
-
public
void
addView
(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
-
applyDefaultToken(params);
-
mGlobal.addView(view, params, mDisplay, mParentWindow);
-
}
之前也分析过WindowManagerGlobal,它有3个重要的成员变量:
-
private final ArrayList<View> mViews =
new ArrayList<View>();
//所有的DecorView对象
-
private final ArrayList<ViewRootImpl> mRoots =
new ArrayList<ViewRootImpl>();
//所有的ViewRootImpl对象
-
private final ArrayList<WindowManager.LayoutParams> mParams =
//所有顶层View的layout参数
-
new ArrayList<WindowManager.LayoutParams>();
我们再来看WindowManagerGlobal的addView函数,这个函数先是查找是否已经在WindowManagerGlobal中已经有这个view,如果有的话就调用其ViewRootImpl的doDie函数中主要是调用WindowManagerGlobal函数去除这个ViewRootImpl对象,在这个主要是创建了ViewRootImpl,并且把DecorView,RootViewRootImpl,layout参数都保存起来了。然后调用了ViewRootImpl的setView函数。
-
public void addView(View view, ViewGroup.LayoutParams params,
-
Display display, Window parentWindow) {
-
......
-
int index = findViewLocked(view,
false);
//查找是否有该view,获取其index
-
if (index >=
0) {
-
if (mDyingViews.contains(view)) {
-
// Don't wait for MSG_DIE to make it's way through root's queue.
-
mRoots.get(index).doDie();
//调用ViewRootImpl的doDie函数
-
}
else {
-
throw
new IllegalStateException(
"View " + view
-
+
" has already been added to the window manager.");
-
}
-
// The previous removeView() had not completed executing. Now it has.
-
}
-
......
-
-
root =
new ViewRootImpl(view.getContext(), display);
//新建ViewRootImpl对象
-
-
view.setLayoutParams(wparams);
-
-
mViews.add(view);
//成员变量增加
-
mRoots.add(root);
-
mParams.add(wparams);
-
}
-
-
// do this last because it fires off messages to start doing things
-
try {
-
root.setView(view, wparams, panelParentView);
//调用ViewRootImpl的setView函数
-
}
catch (RuntimeException e) {
-
// BadTokenException or InvalidDisplayException, clean up.
-
synchronized (mLock) {
-
final
int index = findViewLocked(view,
false);
-
if (index >=
0) {
-
removeViewLocked(index,
true);
-
}
-
}
-
throw e;
-
}
-
}
我们再来看下ViewRootImpl的doDie函数中最后有下面一行代码:
WindowManagerGlobal.getInstance().doRemoveView(this);
而在WindowManagerGlobal中就是去除相关所有的保存。
-
void doRemoveView(ViewRootImpl root) {
-
synchronized (mLock) {
-
final
int index = mRoots.indexOf(root);
-
if (index >=
0) {
-
mRoots.remove(index);
-
mParams.remove(index);
-
final View view = mViews.remove(index);
-
mDyingViews.remove(view);
-
}
-
}
-
if (HardwareRenderer.sTrimForeground && HardwareRenderer.isAvailable()) {
-
doTrimForeground();
-
}
-
}
ViewRootImpl的setView函数
下面我们再来看看ViewRootImpl的setView函数:
-
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
-
synchronized (
this) {
-
if (mView == null) {
-
mView = view;
-
-
mAttachInfo.mDisplayState = mDisplay.getState();
-
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
-
-
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
-
mFallbackEventHandler.setView(view);
-
mWindowAttributes.copyFrom(attrs);
-
if (mWindowAttributes.packageName == null) {
-
mWindowAttributes.packageName = mBasePackageName;
-
}
-
attrs = mWindowAttributes;
-
// Keep track of the actual window flags supplied by the client.
-
mClientWindowLayoutFlags = attrs.flags;
-
-
setAccessibilityFocus(null, null);
-
-
if (view instanceof RootViewSurfaceTaker) {
-
mSurfaceHolderCallback =
-
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
-
if (mSurfaceHolderCallback != null) {
-
mSurfaceHolder =
new TakenSurfaceHolder();
-
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
-
}
-
}
-
-
// Compute surface insets required to draw at specified Z value.
-
// TODO: Use real shadow insets for a constant max Z.
-
if (!attrs.hasManualSurfaceInsets) {
-
final
int surfaceInset = (
int) Math.
ceil(view.getZ() *
2);
-
attrs.surfaceInsets.
set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
-
}
-
-
CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
-
mTranslator = compatibilityInfo.getTranslator();
-
-
// If the application owns the surface, don't enable hardware acceleration
-
if (mSurfaceHolder == null) {
-
enableHardwareAcceleration(attrs);
-
}
-
-
boolean restore =
false;
-
if (mTranslator != null) {
-
mSurface.setCompatibilityTranslator(mTranslator);
-
restore =
true;
-
attrs.backup();
-
mTranslator.translateWindowLayout(attrs);
-
}
-
if (DEBUG_LAYOUT) Log.d(TAG,
"WindowLayout in setView:" + attrs);
-
-
if (!compatibilityInfo.supportsScreen()) {
-
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-
mLastInCompatMode =
true;
-
}
-
-
mSoftInputMode = attrs.softInputMode;
-
mWindowAttributesChanged =
true;
-
mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
-
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();
//绘制UI布局
-
if ((mWindowAttributes.inputFeatures
-
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) ==
0) {
-
mInputChannel =
new InputChannel();
//创建按键通道
-
}
-
try {
-
mOrigWindowType = mWindowAttributes.type;
-
mAttachInfo.mRecomputeGlobalAttributes =
true;
-
collectViewAttributes();
-
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
-
getHostVisibility(), mDisplay.getDisplayId(),
-
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
-
mAttachInfo.mOutsets, mInputChannel);
-
}
catch (RemoteException e) {
-
mAdded =
false;
-
mView = null;
-
mAttachInfo.mRootView = null;
-
mInputChannel = null;
-
mFallbackEventHandler.setView(null);
-
unscheduleTraversals();
-
setAccessibilityFocus(null, null);
-
throw
new RuntimeException(
"Adding window failed", e);
-
} finally {
-
if (restore) {
-
attrs.restore();
-
}
-
}
-
-
if (mTranslator != null) {
-
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
-
}
-
mPendingOverscanInsets.
set(
0,
0,
0,
0);
-
mPendingContentInsets.
set(mAttachInfo.mContentInsets);
-
mPendingStableInsets.
set(mAttachInfo.mStableInsets);
-
mPendingVisibleInsets.
set(
0,
0,
0,
0);
-
if (DEBUG_LAYOUT) Log.v(TAG,
"Added window " + mWindow);
-
if (res < WindowManagerGlobal.ADD_OKAY) {
-
mAttachInfo.mRootView = null;
-
mAdded =
false;
-
mFallbackEventHandler.setView(null);
-
unscheduleTraversals();
-
setAccessibilityFocus(null, null);
-
switch (res) {
-
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
-
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
-
throw
new WindowManager.BadTokenException(
-
"Unable to add window -- token " + attrs.token
-
+
" is not valid; is your activity running?");
-
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
-
throw
new WindowManager.BadTokenException(
-
"Unable to add window -- token " + attrs.token
-
+
" is not for an application");
-
case WindowManagerGlobal.ADD_APP_EXITING:
-
throw
new WindowManager.BadTokenException(
-
"Unable to add window -- app for token " + attrs.token
-
+
" is exiting");
-
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
-
throw
new WindowManager.BadTokenException(
-
"Unable to add window -- window " + mWindow
-
+
" has already been added");
-
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
-
// Silently ignore -- we would have just removed it
-
// right away, anyway.
-
return;
-
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
-
throw
new WindowManager.BadTokenException(
-
"Unable to add window " + mWindow +
-
" -- another window of this type already exists");
-
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
-
throw
new WindowManager.BadTokenException(
-
"Unable to add window " + mWindow +
-
" -- permission denied for this window type");
-
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
-
throw
new WindowManager.InvalidDisplayException(
-
"Unable to add window " + mWindow +
-
" -- the specified display can not be found");
-
case WindowManagerGlobal.ADD_INVALID_TYPE:
-
throw
new WindowManager.InvalidDisplayException(
-
"Unable to add window " + mWindow
-
+
" -- the specified window type is not valid");
-
}
-
throw
new RuntimeException(
-
"Unable to add window -- unknown error code " + res);
-
}
-
-
if (view instanceof RootViewSurfaceTaker) {
-
mInputQueueCallback =
-
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
-
}
-
if (mInputChannel != null) {
-
if (mInputQueueCallback != null) {
-
mInputQueue =
new InputQueue();
-
mInputQueueCallback.onInputQueueCreated(mInputQueue);
-
}
-
mInputEventReceiver =
new WindowInputEventReceiver(mInputChannel,
//创建按键应用层接受对象
-
Looper.myLooper());
-
}
-
-
view.assignParent(
this);
-
mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) !=
0;
-
mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) !=
0;
-
-
if (mAccessibilityManager.isEnabled()) {
-
mAccessibilityInteractionConnectionManager.ensureConnection();
-
}
-
-
if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
-
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-
}
-
-
// Set up the input pipeline.
-
CharSequence counterSuffix = attrs.getTitle();
-
mSyntheticInputStage =
new SyntheticInputStage();
//按键的一些流程类
-
InputStage viewPostImeStage =
new ViewPostImeInputStage(mSyntheticInputStage);
-
InputStage nativePostImeStage =
new NativePostImeInputStage(viewPostImeStage,
-
"aq:native-post-ime:" + counterSuffix);
-
InputStage earlyPostImeStage =
new EarlyPostImeInputStage(nativePostImeStage);
-
InputStage imeStage =
new ImeInputStage(earlyPostImeStage,
-
"aq:ime:" + counterSuffix);
-
InputStage viewPreImeStage =
new ViewPreImeInputStage(imeStage);
-
InputStage nativePreImeStage =
new NativePreImeInputStage(viewPreImeStage,
-
"aq:native-pre-ime:" + counterSuffix);
-
-
mFirstInputStage = nativePreImeStage;
-
mFirstPostImeInputStage = earlyPostImeStage;
-
mPendingInputEventQueueLengthCounterName =
"aq:pending:" + counterSuffix;
-
}
-
}
-
}
这个函数主要是调用了requestLayout函数来对应用窗口的UI布局,然后创建了InputChannel。调用ViewRoot类的静态成员变量sWindowSession所描述的一个类型为Session的Binder代理对象的成员函数add来请求WindowManagerService增加一个WindowState对象,以便可以用来描述当前正在处理的一个ViewRootImpl所关联的一个应用程序窗口。
最后创建了WindowInputEventReceiver应用层的按键接受,以及一些按键在应用层的流程的相关类。