上一章 Android理解 Window 与 WindowManager,我们已经学习了 Window - ViewRootImpl - View 之间的关系,也知道 Window 实际是通过 WindowManagerService 去创建的。
那么 Activity 的 window 是怎么创建的呢?
首先,从 Activity启动流程 (基于8.0源码) 一文中,我们已经知道,Activity 的创建,最后会通过 ActivityThread 的 handleLaunchActivity(),这个时候 activity 才真正开始创建。如下:
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
...
WindowManagerGlobal.initialize();
// Hint the GraphicsEnvironment that an activity is launching on the process.
GraphicsEnvironment.hintActivityLaunch();
final Activity a = performLaunchActivity(r, customIntent);
....
return a;
}
可以看到,它先对 WindowManagerGlobal 进行初始化,接着调用 performLaunchActivity() 方法:
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
....
if (activity != null) {
...
appContext.setOuterContext(activity);
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, window, r.configCallback,
r.assistToken);
...
return activity;
}
#Activity#attach
@UnsupportedAppUsage
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,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
// 通过 PhoneWindow 创建 window
mWindow = new PhoneWindow(this, window, activityConfigCallback);
//设置回调,比如 onAttachToWindow()
mWindow.setWindowControllerCallback(this);
...
//设置 windowmanager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
...
}
在这个方法中,它通过 activity.attach() 实现了 window 的创建,而在 activity#attach() 中,创建了一个 Window;其实 window 已经被创建了,但是它只是一个空壳子,我们知道,window 只有与 view 绑定,才有意义。
Activity view 的创建是通过 setContentView 来设置的,现在的Activity 都是继承AppcompatActivity,它会对使用代理类对 Activity 进行一些数据封装,这里为了更方便的了解 Activity 的创建,让 MainActivity 继承 Activity ,然后查看 setContentView :
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
从 Activity 创建流程知道,getWindow() 为 PhoneWindow,所以,可以直接去到 PhoneWindow 的 setContentView :
public void setContentView(int layoutResID) {
// 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)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
// 解析 layoutId 中的子 view
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParentExplicitlySet = true;
}
其中 installDecor() 为关键方法,它会完成以下功能:
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//创建 DecorView
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) {
//创建父布局,设置 window 背景等
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 {
// 是否隐藏 title
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);
}
}
}
...
}
}
}
其中 generateDecor() 比较简单,就是 创建 DectorView :
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.
Context context;
....
return new DecorView(context, featureId, this, getAttributes());
}
来重点看一下 generateLayout() 方法,它的功能如下:
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
// 解析style 的属性,比如判断是否有 actionbar
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
...
if (getContainer() == null) {
if (mBackgroundDrawable == null) {
if (mFrameResource == 0) {
mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
}
//拿到背景
if (a.hasValue(R.styleable.Window_windowBackground)) {
mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);
}
}
...
}
// Inflate the window decor.
// 初始化 DecorView 布局
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_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();
//解析根布局的view
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 拿到 R.id.content 的值
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
if (getContainer() == null) {
//先设置背景
mDecor.setWindowBackground(mBackgroundDrawable);
...
return contentParent;
}
mDecor.onResourcesLoaded() 回去解析真正的根布局,在此之前,它会根据主题,去解析到底使用哪个 layoutId,比如使用 R.layout.screen_simple (在frameworks\base\core\res\res\layout下),它其实为一个 LinearLayout,分为 bar 和 content 两个部分:
其实,我们用 tool - layout inspector 也可以看出来, DecorView 也是由上面两个部分组成的:
用一张简单的图,就是如下所示:
这样,根布局的 DecorView 就设置好了,接着会调用 mLayoutInflater.inflate(layoutResID, mContentParent) 方法,去解析 Activity 的layoutId,关于这部分的解析,可以参考:Android 换肤原理解析
上面只是让 DecorView 填充了内容,但是还未显示出来,这时还需要 WindowManagerService 的 addView 方法,将其 Window 和 View 关联起来,而在 ActivityThread 的 handleResumeActivity 方法中,有如下代码:
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// TODO Push resumeArgs into the activity for consideration
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
if (r.window == null && !a.mFinished && willBeVisible) {
// 拿到 window
r.window = r.activity.getWindow();
// 拿到 decorview
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 (r.mPreserveWindow) {
//拿到 ViewRootImpl
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 通过 Windowmanager 的 addView ,把 window 和view 关联起来
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
....
}
到这里,Window 中 终于被 WindowManager 接管了,接下来的 Window 和 VIew 的关联,可以参考上一章Android理解 Window 与 WindowManager 。
这里做一下总结:
参考Android 艺术开发