根据前面的分析,我们可以知道,每一个Activity都关联了一个Window对象,用来描述一个应用程序窗口。每一个应用程序窗口内部又包含一个View对象,用于描述应用程序视图。应用程序视图是真正用来实现UI和布局的。也就是说,每一个Activity的UI内容和布局都是通过与其所关联的一个Window对象的内部一个View对象实现的。
还记得Activity的启动流程嘛?在ActivityThread.performLaunchActivity(ActivityClientRecord r, Intent customIntent)
中,先创建了Context,然后是创建了一个Activity,然后在对Activity的初始化中又创建了一个Window,之后会去调用Activity.onCreate()
方法,我们从这里开始分析:
一般我们会自定义Activity,调用的onCreate
方法,也就是我们自己实现的onCreate
方法
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在onCreate方法中,会调用父类的onCreate
方法,最终到达Activity.onCreate()
,然后会调用setContentView(R.layout.activity_main);
进行视图的创建:
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}
//AppCompatDelegate.java
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else {
return new AppCompatDelegateImplV14(context, window, callback);
}
}
在这里,我们遇到一个新的类:AppCompatDelegate.java
public abstract class AppCompatDelegate
我们看看它的介绍:
This class represents a delegate which you can use to extend AppCompat’s support to any {@link android.app.Activity}.
上面大概是说,它是代表的是一个委托,你可以使用该委托将AppCompat的支持扩展到任何Activity。
继续看,当AppCompatDelegate对象建立完了后,会调用setContentView()
方法,我这里使用的api是27,所以返回的AppCompatDelegate对象是AppCompatDelegateImplV14,而AppCompatDelegateImplV14继承自AppCompatDelegateImplV9:
class AppCompatDelegateImplV14 extends AppCompatDelegateImplV9
AppCompatDelegateImplV14(Context context, Window window, AppCompatCallback callback) {
super(context, window, callback);
}
AppCompatDelegateImplV9(Context context, Window window, AppCompatCallback callback) {
super(context, window, callback);
}
setContextView方法是在AppCompatDelegateImplV9这个类中实现的。
//AppCompatDelegateImplV9.java
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
mOriginalWindowCallback.onContentChanged();
}
我们先看ensureSubDecor()
方法:
private void ensureSubDecor() {
//如果mSubDecor还没有初始化,就创建SubDecor
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
// If a title was set before we installed the decor, propagate it now
//如果在decor安装前有设置title,现在就 把它传进去。
CharSequence title = getTitle();
if (!TextUtils.isEmpty(title)) {
onTitleChanged(title);
}
//适配尺寸在屏幕上,一些参数的初始化
applyFixedSizeWindow();
//空实现
onSubDecorInstalled(mSubDecor);
mSubDecorInstalled = true;
// Invalidate if the panel menu hasn't been created before this.
// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
// being called in the middle of onCreate or similar.
// A pending invalidation will typically be resolved before the posted message
// would run normally in order to satisfy instance state restoration.
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!isDestroyed() && (st == null || st.menu == null)) {
invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
}
}
}
在上面的方法中,如果SubDecor没有被创建过,就先创建:
//AppCompatDelegateImplV9.java
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
//下面省略的代码中,获取了系统的主题。
... ...
// Now let's make sure that the Window has installed its decor by retrieving it
//保证window是被加载了的
//在这里也确保DecorView的初始化
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
//根据属性,创建不同的subDecor
if (!mWindowNoTitle) {
//如果Window没有Titile
if (mIsFloating) {
//是否悬浮,也就是说该Activity可以使dialog的样子
// If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {
//是否有ActionBar
TypedValue outValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
Context themedContext;
if (outValue.resourceId != 0) {
themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
} else {
themedContext = mContext;
}
// Now inflate the view using the themed context and set it as the content view
subDecor = (ViewGroup) LayoutInflater.from(themedContext)
.inflate(R.layout.abc_screen_toolbar, null);
mDecorContentParent = (DecorContentParent) subDecor
.findViewById(R.id.decor_content_parent);
mDecorContentParent.setWindowCallback(getWindowCallback());
/**
* Propagate features to DecorContentParent
*/
if (mOverlayActionBar) {
mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (mFeatureProgress) {
mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
}
if (mFeatureIndeterminateProgress) {
mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
}
}
} else {
if (mOverlayActionMode) {
//是叠加模式,用于设置ActionBar的主题
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_screen_simple_overlay_action_mode, null);
} else {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
if (Build.VERSION.SDK_INT >= 21) {
// If we're running on L or above, we can rely on ViewCompat's
// setOnApplyWindowInsetsListener
ViewCompat.setOnApplyWindowInsetsListener(subDecor,
new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
final int top = insets.getSystemWindowInsetTop();
final int newTop = updateStatusGuard(top);
if (top != newTop) {
insets = insets.replaceSystemWindowInsets(
insets.getSystemWindowInsetLeft(),
newTop,
insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
}
// Now apply the insets on our view
return ViewCompat.onApplyWindowInsets(v, insets);
}
});
} else {
// Else, we need to use our own FitWindowsViewGroup handling
((FitWindowsViewGroup) subDecor).setOnFitSystemWindowsListener(
new FitWindowsViewGroup.OnFitSystemWindowsListener() {
@Override
public void onFitSystemWindows(Rect insets) {
insets.top = updateStatusGuard(insets.top);
}
});
}
}
if (subDecor == null) {
throw new IllegalArgumentException(
"AppCompat does not support the current theme features: { "
+ "windowActionBar: " + mHasActionBar
+ ", windowActionBarOverlay: "+ mOverlayActionBar
+ ", android:windowIsFloating: " + mIsFloating
+ ", windowActionModeOverlay: " + mOverlayActionMode
+ ", windowNoTitle: " + mWindowNoTitle
+ " }");
}
if (mDecorContentParent == null) {
mTitleView = (TextView) subDecor.findViewById(R.id.title);
}
// Make the decor optionally fit system windows, like the window's decor
ViewUtils.makeOptionalFitsSystemWindows(subDecor);
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);
//得到Window的ContentView
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to
// migrate them to our content view
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
// Change our content FrameLayout to use the android.R.id.content id.
// Useful for fragments.
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);
// The decorContent may have a foreground drawable set (windowContentOverlay).
// Remove this as we handle it ourselves
if (windowContentView instanceof FrameLayout) {
((FrameLayout) windowContentView).setForeground(null);
}
}
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);
contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
@Override
public void onAttachedFromWindow() {}
@Override
public void onDetachedFromWindow() {
dismissPopups();
}
});
return subDecor;
}
@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
//获取DecorView,然后他是一个FrameLayout
installDecor();
}
return mDecor;
}
在上面代码中会根据不同的属性,加载不同的subDecor,然后看Window中的contentView中有没有子View,如果有就从contentView中移除并加入到subDecor的contentView中。
然后调用了mWindow.setContentView(subDecor);
//PhoneWindow.java
@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@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)) {
//FEATURE_CONTENT_TRANSITIONS
//用于请求窗口内容更改的标志应使用TransitionManager进行动画处理。
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//有动画转场添加
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
//一般会到这里
//添加subDecor
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
在上面中,如果mContentParent==null,那么就会调用installDecor()
方法,但是在前面有调用PhoneWindow的getDecorView
方法中,会调用installDecor
方法。mContentParent用来描述一个类型为DecorView的视图对象,或者类型为DecorView的视图对象的一个子视图对象,用作UI容器。当它的值为null的时候,说明正在处理的应用程序创建的视图还没有创建.
我们看看installDecor
方法:
//PhoneWindow.java
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
private TextView mTitleView;
DecorContentParent mDecorContentParent;
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
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) {
mDecorContentParent = decorContentParent;
mDecorContentParent.setWindowCallback(getCallback());
if (mDecorContentParent.getTitle() == null) {
mDecorContentParent.setWindowTitle(mTitle);
}
... ...
} else {
mTitleView = findViewById(R.id.title);
if (mTitleView != null) {
if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
final View titleContainer = findViewById(R.id.title_container);
if (titleContainer != null) {
titleContainer.setVisibility(View.GONE);
} else {
mTitleView.setVisibility(View.GONE);
}
mContentParent.setForeground(null);
} else {
mTitleView.setText(mTitle);
}
}
}
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
... ...
}
}
由于我们是在Activity启动中分析的,所以在这里我们可以假设mDecor是为null,因此会调用generateDecor()
方法来创建DecorView:
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
/*
*系统是没有上下文的,在这种情况下,我们需要直接使用我们拥有的上下文,
*否则,我们需要application 的coontext,
*因此,我们不依赖于activity
*/
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
在上面代码中,我们可以看到,重点就是调用了DecorView的构造方法。
回到PhoneWindow类的installDecor
方法,在创建成功DecorView之后,由于mContentParent也是null,所以会调用generateLayout(mDecor)
来给mContentParent赋值:
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
... ...
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} 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) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
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);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks(contentParent);
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
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);
}
mDecor.finishChanging();
return contentParent;
}
在这个方法中,会根据当前应用程序创建的Feature来加载对应的窗口布局文件,这些布局文件保存在frameworks/base/core/res/res/layout目录下,它们必须包含一个id值为“content”的布局控件。这个布局控件必须要从ViewGroup继承下来,作为窗口的UI容器。
回到PhoneWindow类中的installDecor
方法
在generateLayout方法
执行完了之后,就将这个id值为“content”的ViewGroup返回给PhoneWinodw类的mContentParent。
之后会检查前面加载的窗口布局文件是否包含一个id值为“titile”的TextView控件,如果包含,就会给mTextView赋值,用来描述当前应用程序的标题栏。但是,如果当前应用程序没有标题栏,即它的Feature位FEATURE_NO_TITLE的值等于1,那么就需要将前面得到的标题栏隐藏起来。
注意:mTitleView所描述的标题栏有可能是包含在一个id值为“title_container”的容器里面的,在这种情况下,就需要隐藏该标题栏容器。另一方面,如果当前应用程序窗口是设置有标题栏的,那么PhoneWindow类中的installDecor
方法中就会设置它的标题栏文字。应用程序窗口的标题栏文字保存在PhoneWindow类的成员变量mTitle中,我们可以调用PhoneWindow类的成员函数setTitle来设置。
在setContentView
()方法中,会将创建的subDecor添加到mContentParent中。
然后回到AppCompatDelegateImplV9的setContentView()
方法:
//AppCompatDelegateImplV9.java
final Window.Callback mOriginalWindowCallback;
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
mOriginalWindowCallback.onContentChanged();
}
在ensureSubDecor()
方法调用完毕后,这个时候,subDecor创建成果,并且创建了DecorVIew,得到了mContentParent,将subDecor添加进去了。之后会通过mSubDecor得到contentParent,将我们自己的布局添加进去。
在这个方法最后,调用了mOriginalWindowCallback.onContentChanged();
,mOriginalWindowCallback是什么呢?在AppCompatDelegateImplV9对象创建的时候会调用父类的构造方法:
AppCompatDelegateImplBase(Context context, Window window, AppCompatCallback callback) {
mContext = context;
mWindow = window;
mAppCompatCallback = callback;
mOriginalWindowCallback = mWindow.getCallback();
if (mOriginalWindowCallback instanceof AppCompatWindowCallbackBase) {
throw new IllegalStateException(
"AppCompat has already installed itself into the Window");
}
mAppCompatWindowCallback = wrapWindowCallback(mOriginalWindowCallback);
// Now install the new callback
mWindow.setCallback(mAppCompatWindowCallback);
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(
context, null, sWindowBackgroundStyleable);
final Drawable winBg = a.getDrawableIfKnown(0);
if (winBg != null) {
mWindow.setBackgroundDrawable(winBg);
}
a.recycle();
}
可是看到,mOriginalWindowCallback = mWindow.getCallback();
,所以对于现在分析的Activity启动流程来说,mOriginalWindowCallback 就是我们正在启动的Activity。
所以最后是调用它的方法来通知content改变了。
这一步执行完成之后,应用程序窗口视图就创建完成了。
然后我们回到ActivityThread中,在performLaunchActivity
方法中,调用完Activity.onCreate方法后,我们知道,肯定开始Activity的生命周期,我们继续看看handleResumeActivity()
方法
//ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
... ...
// TODO Push resumeArgs into the activity for consideration
//调用Activity的onResume方法
//返回一个ActivityClientRecord对象,
//它的一个成员变量activity描述的就是正在激活的Activity
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
... ...
//是否可见
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManager.getService().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//在Activity初始化后,并没有给r.window赋值
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) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
... ...
a.onWindowAttributesChanged(l);
}
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
... ...
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManager.getService()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
Activity类中有一个成员变量mStartedActivity用来描述一个Activity是否正在启动另外一个Activity,并且等待这个新的Activity的执行结果。如果正在启动,那么mStartedActivity就等于ture,表示在新的activity组件的执行结果返回之前,当前Activity要保持不可见的状态。
如果Activity的成员变量mStartedActivity等于ture,它接下来的状态是保持不可见,但是有可能它所启动的Activity的UI不是全屏的。在这种情况下,Activity的UI仍是部分可见的,这时候也要将变量willBeVisible的值设置为true。因此,如果前面得到变量willBeVidible的值等于false,那么在handleResumeActivity
方法中,就会通过Binder进程间通信机制来调用ActivityManagerService的willActivityBeVisible
方法来检查在该Activity上面的其他Activity是否是全屏的。如果不是,那么willActivityBeVisible()
方法就会返回ture,表明需要显示该Activity。
接下来,如果r.window == null
,表示当前正在激活的Activity所关联的应用程序窗口对象还没有关联到一个ViewRoot对象,进一步,如果该Activity还活着,并且接下来是可见的:
(r.window == null && !a.mFinished && willBeVisible)
那么这时候就说明需要为与该Activity所关联的应用程序窗口对象关联一个ViewRootImpl对象。
将一个视图对象与一个ViewRootImpl对象关联是通过WindowManager来执行的。
if (r.window == null && !a.mFinished && willBeVisible) {
//获取应用程序窗口
r.window = r.activity.getWindow();
//获取应用程序视图
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//得到WinodwManager
ViewManager wm = a.getWindowManager();
//获取WindowManager.LayoutParams的对象
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
... ...
//当前正在激活的Activity在本地进程中是否可见
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//关联应用程序窗口视图对象和ViewRootImpl
wm.addView(decor, l);
} else {
... ...
a.onWindowAttributesChanged(l);
}
}
}
看了上面的代码,我们知道wm是WindowManagerImpl类型的:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
继续看:
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
/*
*参数view和参数params描述的就是要关联的view对象和LayoutParams对象
*/
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
... ...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
... ...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
//检查参数view所描述的一个view对象是否在mViews中,
//如果存在,就说明该view已经关联过ViewRoot对象和LayoutParams对象了。
//如果存在,则让它死亡
int index = findViewLocked(view, false);
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();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
//检查view是否描述的是一个子应用程序的视图对象时,也就是满足下面这个条件
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
//找到这个子视图对象的父视图对象 panelParentView
final int count = mViews.size();
for (int i = 0; i < count; i++) {
//wparams.token指向了一个类型为W的Binder本地对象的一个IBinder接口,
//用来描述参数view所描述的子应用程序视图对象所属的父应用程序窗口视图对象
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
//创建ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//并分别保存在list中
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);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
通过上面的代码,我们就知道WindowManagerImpl是怎么关联一个应用程序窗口视图对象(DecorView)和ViewRootImpl对象。
一个View对象在于ViewRootImpl对象关联的同时,会关联一个WindowManager.LayoutParams对象,这个LayoutParams对象是用来描述应用程序窗口视图的布局属性的。
WindowMamagerImpl类中的三个成员变量:mViews,mRoots和mParams分别存储View,ViewRootImpl,LayoutParams对象,这三个list大小适中保持相等,也就是说相同位置上的View,ViewRootImpl,LayoutParams是有关联的。这样就关联好了。
代码的最后会调用:root.setView(view, wparams, panelParentView);
在看这个之前,先看一下ViewRootImpl的介绍:
The top of a view hierarchy, implementing the needed protocol between View and the WindowManager
视图层次结构的顶部,实现了View和WindowManager之间需要的协议。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks
那么我们看它的setView
方法把:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
//保存view
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
... ...
mWindowAttributes.copyFrom(attrs);
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
attrs = mWindowAttributes;
setTag();
... ...
// 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);
mSurfaceHolder.addCallback(mSurfaceHolderCallback);
}
}
// Compute surface insets required to draw at specified Z value.
// TODO: Use real shadow insets for a constant max Z.
... ...
boolean restore = false;
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
//保存view
mAttachInfo.mRootView = view;
mAttachInfo.mScalingRequired = mTranslator != null;
mAttachInfo.mApplicationScale =
mTranslator == null ? 1.0f : mTranslator.applicationScale;
if (panelParentView != null) {
//如果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();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//增加一个WindowSate对象
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();
}
}
... ...
}
}
}
参数view所描述的一个View对象会分别保存在ViewRootIml类中的mView和mAttachInfo中的mRootView中,而参数attrs所描述的一个WindowManager.LayoutParams对象的内容会被拷贝到ViewRootImpl类中的mWinodwAttributes中。
当参数panelParentView的值不等于null的时候,就表示参数view描述的是一个子应用程序窗口视图对象,在这种情况下,panelParentView描述的就是一个父应用程序窗口视图对象,这个时候,我们就需要获取用来描述父应用程序窗口视图对象的一个类型为W的Binder本地对象的IBinder接口,保存在mAttachInfo中的mPanelParentWindowToken中,这样以后就可以知道ViewRootImpl中的mView所描述的子应用程序窗口视图的父应用程序窗口视图。
在处理完上面的步骤后,mAdd的值就为true了, 表示当前正在处理的ViewRooImpl对象已经关联好了一个View对象了。
接下来,ViewRootImpl中的setView方法还需要执行两个操作:
第一个是: requestLayout();
,请求对应用程序窗口的UI做第一次布局。
第二个是:
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
mWindowSession是在ViewRootImpl构造方法中被初始化的:
mWindowSession = WindowManagerGlobal.getWindowSession();
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
//WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
调用mWindowSession.addToDisplay
方法,来请求WindowManagerService增加一个WindowState对象,返回的res 可以用来描述当前正在处理的一个ViewRootImpl所关联的一个应用程序窗口。
到这里,就分析完了:
总结: