参考文章
什么是 Window
window
顾名思义就是窗口的意思,在 android
最终图形的绘制都是通过window
进行绘制的。Window
是View
的管理者,不论是 Activity
,Toast
,Dialog
最终都是将其View
附加在Window
上的。
Window的创建过程
Activity 中 Window创建过程
在 Activity
的生命周期之中,onCreate 方法并不是第一个被调用的。实际上第一个被调用的方法是 attach
方法。
在ActivityThread.java#performLaunchActivity
方法中,acitivty
实例被创建后,先调用attach
方法再调用了onCreate
方法.
ActivityThread.java
中代码如下(注意:以下代码有删减,只保留了大致流程代码)
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity =mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
Configuration config = new Configuration(mCompatConfiguration);
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);
...
if (r.isPersistable()) {
//系统关机后保存数据的能力,暂时不太理解其作用,
//需要在清单文件中对Acitivty配置persistableMode属性
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {//一般都是走到这里
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
return activity;
}
callActivityOnCreate方法内部流程为 callActivityOnCreate->Activity#performCreate
->Activity#onCreate
接下来,让我们看下 attach
方法
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) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
// activityConfigCallback 用于监听配置改变,该参数最终被传入ViewRootImpl#setActivityConfigCallback
mWindow = new PhoneWindow(this, window, activityConfigCallback);
// 在状态栏,画中画等模式改变时调用
mWindow.setWindowControllerCallback(this);
// this 传入的是Window.Callback
// 界面有关的callback调用,window属性更改,contentView改变等
// 以及事件的分发
mWindow.setCallback(this);
//窗口消失时回调 onWindowDismissed,在Activity中会调用finish
mWindow.setOnWindowDismissedCallback(this);
// 设置 根View的构造方法,Activity#onCreateView
mWindow.getLayoutInflater().setPrivateFactory(this);
// 将输入法模式显示状态设置为不指定,可能显示可能不显示
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
// 暂时不知道作用
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
// 获取当前UI线程
mUiThread = Thread.currentThread();
// 当前主线程 一般而言 mUiThread = mMainThread
mMainThread = aThread;
// 构造
mInstrumentation = instr;
/*
token是一个binder代理,一个Token类,继承自IApplicationToken.Stub ,用于标识activity
token只有一个方法getName
*/
mToken = token;
// ident值为0
mIdent = ident;
// 当前应用对象
mApplication = application;
// 传入的intent
mIntent = intent;
// 调用者 信息
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
//默认标题
mTitle = title;
// 基本为null(Fragment出现后,这个属性就基本被弃用了)
mParent = parent;
// 不知道作用
mEmbeddedID = id;
// 不知
mLastNonConfigurationInstances = lastNonConfigurationInstances;
// mVoiceInteractor 语音交互
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
// setWindowManager 将WindowManger传入
// WindowManager是个interface,其实现类是 WindowManagerImp
// 而WindowMangerImpl是个代理类,最终是通过WindowMnagerGlobal进行各种窗口的操作。而WindowManagerGlobal是一个进程内的单例类。
// FLAG_HARDWARE_ACCELERATED 判断Activity是否开启了硬件加速
// 关于加速,可以参考文章
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//如果有mParent,则当前 Activity窗口的DecorView不作为顶级窗口,而是根据其父容器决定如果自身如何显示
//否则,Activity的的DecorView将作为顶层Window
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
// 获取得到 Acitivty对应的`WindowManagerImpl`对象
mWindowManager = mWindow.getWindowManager();
// 当前的一些配置,如 横竖平方向等
mCurrentConfig = config;
//设置位图模式,具体可参考 https://developer.android.google.cn/training/wide-color-gamut?hl=zh-cn
mWindow.setColorMode(info.colorMode);
setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
enableAutofillCompatibilityIfNeeded();
}
首先可见在attach
方法中直接new
一个 PhoneWindow
实例 ,
mWindow = new PhoneWindow(this, window, activityConfigCallback);
具体PhoneWindow
创建如下
/**
* Constructor for main window of an activity.
*/
public PhoneWindow(Context context, Window preservedWindow,
ActivityConfigCallback activityConfigCallback) {
this(context);
// Only main activity windows use decor context, all the other windows depend on whatever
// context that was given to them.
mUseDecorContext = true;
if (preservedWindow != null) {
mDecor = (DecorView) preservedWindow.getDecorView();
mElevation = preservedWindow.getElevation();
mLoadElevation = false;
mForceDecorInstall = true;
// If we're preserving window, carry over the app token from the preserved
// window, as we'll be skipping the addView in handleResumeActivity(), and
// the token will not be updated as for a new window.
getAttributes().token = preservedWindow.getAttributes().token;
}
// Even though the device doesn't support picture-in-picture mode,
// an user can force using it through developer options.
boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_PICTURE_IN_PICTURE);
mActivityConfigCallback = activityConfigCallback;
}
mWindow.setCallback(this)
处设置了Window
的 Callback
回调,其大多与输入触摸等事件相关,具体回调方法如下:
public interface Callback {
public boolean dispatchKeyEvent(KeyEvent event);
public boolean dispatchKeyShortcutEvent(KeyEvent event);
public boolean dispatchTouchEvent(MotionEvent event);
public boolean dispatchTrackballEvent(MotionEvent event);
public boolean dispatchGenericMotionEvent(MotionEvent event);
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
......
setWindowManager 处有两点需要分析
getSystemService
获取的对象setWindowManager
的内部操作
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
1. getSystemService获取的对象
context
传入的是 一个ContextImple
对象,其 getSystemService
内部实现如下
@Override
public Object getSystemService(String name) {
if (vmIncorrectContextUseEnabled()) {
...
}
return SystemServiceRegistry.getSystemService(this, name);
}
而在 SystemServiceRegistry
类中,静态构建了一个 WindowManagerImpl
对象,因此显然 Activity#attach
方法中setWindowManager
传入的是一个WindowManagerImpl
对象。
static{
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
}
/**
* Statically registers a system service with the context.
* This method must be called during static initialization only.
*/
private static <T> void registerService(@NonNull String serviceName,
@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
}
2.setWindowManager 的内部操作
setWindowManager
方法,将 创建一个WindowManagerImpl
对象用于管理Window
,且将传入的WindowManagerImpl
实例作为 其 mParentWindow
属性
显然 getSystemService
中得到的WindowManagerImpl
实例是所有其他的Acitivty
的WindowManagerImpl
对象的mParentWindow
属性。
Window.java
代码如下
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
WindowManagerImpl.java
代码如下:
public final class WindowManagerImpl implements WindowManager {
@UnsupportedAppUsage
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@VisibleForTesting
public final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
public WindowManagerImpl(Context context) {
this(context, null);
}
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
return new WindowManagerImpl(displayContext, mParentWindow);
}
...
}
以上代码说明了Activity
中 Window
的创建过程,接下来分析 View
添加到Window
的过程
View添加到Window的过程
Activity 中 View添加到 Window 的过程
在 Window
添加完成后,基本就会立即开始调用onCreate方法
,在onCreate
中进行View
的添加。
在文章开头介绍的第一段 performLaunchActivity
方法中代码可见,在调用attach方法后,之后就会调用mInstrumentation.callActivityOnCreate
方法,在调用Activity#performCreate
方法,走到Activity#onCreate
方法
Instrumentation.java#callActivityOnCreate
代码如下:
public void callActivityOnCreate(Activity activity, Bundle icicle,
PersistableBundle persistentState) {
prePerformCreate(activity);
activity.performCreate(icicle, persistentState);
postPerformCreate(activity);
}
Activity.java#performCreate
代码如下:
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
...
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);//走到onCreate处
}
...
}
DecorView 的创建
首先Activity#onCreate
中调用了setContentView
方法
Activity#setContentView
方法实际还是调用了Window#setContentView
方法
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow().setContentView
这段调用内容如下:
PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private DecorView mDecor;
ViewGroup mContentParent;
...
@Override
public void setContentView(int layoutResID) {
//代码1
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 {
//代码2
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
//代码 3
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
}
- 代码1处首先会判断
mContentParent
是否为null
,如为null
,则调用
mContentParent
是PhoneWindow中一个重要属性。 mContentParent 是 Window 放置 View 内容的容器,可能是 mDecor 本身,或者是 mDecor 的一个子 View。(mDecor
是顶级View
,DecorView
继承了FragmentLayout
类)
-
代码2处会通过 mLayoutInflater 将传入的 layoutResID 解析并放入 mContentParent 内。
-
代码3处得到设置的 Callback 回调对象,并调用 onContentChanged(),通知 View 内容发生变化。
此处
Callback
对象就是在Activity#attach
方法中设置的Callback
回调对象. onContentChanged onContentChanged()是Activity中的一个回调方法 当Activity的布局改动时,即setContentView()或者addContentView()方法执行完毕时就会调用该方法, 例如,Activity中各种View的findViewById()方法都可以放到该方法中。当然,一般而言,大多数开发者都是在onCreate
方法中,执行setContentView
后 获取对象实例。
接下来看下 installDecor
方法
installDecor 实现
private void installDecor() {
mForceDecorInstall = false;
//代码1
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);
}
//代码2
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeFrameworkOptionalFitsSystemWindows();
final DecorContentParent decorContentParent =
(DecorContentParent) mDecor.findViewById(R.id.decor_content_parent);
if (decorContentParent != null) {
mDecorContentParent = decorContentParent;
mDecorContentParent.setWindowCallback(getCallback());
...
} else {
mTitleView = findViewById(R.id.title);
...
}
...
}
}
- 代码1 处,调用
generateDecor
方法生成一个mDecor
对象,DecorView
的构造方法中将 调用setWindow
方法,将PhoneWindow
对象传入。
generateDecor
方法
- 代码2 处 将调用
generateLayout
生成mContentParent
对象generateDecor
代码如下,没有太多需要介绍的
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;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, this);
if (mTheme != -1) {
context.setTheme(mTheme);//设置主题, AndroidManifest 设置的主题在 ActivityThread#performLaunchActivity中赋值
}
}
} else {//
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
generateLayout
代码如下
protected ViewGroup generateLayout(DecorView decor) {
// 根据当前的theme 来设置
TypedArray a = getWindowStyle();
//判断是否是悬浮窗
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
...
// 根据 theme 判断是否添加 actionBar、title
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);
...
WindowManager.LayoutParams params = getAttributes();
//是否绘制 状态栏、导航栏 背景
if (!mIsFloating) {
if (!targetPreL && a.getBoolean(
R.styleable.Window_windowDrawsSystemBarBackgrounds,
false)) {
setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
}
if (mDecor.mForceWindowDrawsBarBackgrounds) {
params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
}
}
//是否将statusbar 和navigationBar绘制为明亮主题
if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
decor.setSystemUiVisibility(
decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
decor.setSystemUiVisibility(
decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
//当前模式是否为刘海屏模式,获得刘海屏的模式
if (a.hasValue(R.styleable.Window_windowLayoutInDisplayCutoutMode)) {
int mode = a.getInt(R.styleable.Window_windowLayoutInDisplayCutoutMode, -1);
if (mode < LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
|| mode > LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
throw new UnsupportedOperationException("Unknown windowLayoutInDisplayCutoutMode: "
+ a.getString(R.styleable.Window_windowLayoutInDisplayCutoutMode));
}
params.layoutInDisplayCutoutMode = mode;
}
...
//当前是否是输入法模式
if (!hasSoftInputMode()) {
params.softInputMode = a.getInt(
R.styleable.Window_windowSoftInputMode,
params.softInputMode);
}
//窗口动画
if (params.windowAnimations == 0) {
params.windowAnimations = a.getResourceId(
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 (mFrameResource == 0) {
mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0);
}
if (a.hasValue(R.styleable.Window_windowBackground)) {
mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground);
}
}
if (a.hasValue(R.styleable.Window_windowBackgroundFallback)) {
mBackgroundFallbackDrawable =
a.getDrawable(R.styleable.Window_windowBackgroundFallback);
}
if (mLoadElevation) {
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);
}
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// 代码1
// System.out.println("Features: 0x" + Integer.toHexString(features));
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();
// 代码2
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// ID_ANDROID_CONTENT = com.android.internal.R.id.content
// findViewById 是从 mDecor 中查找View对象
//代码3
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);
}
}
// 如果 getContainer == null ,说明其是顶层View,一般Activity的 Window对应的都是顶层Window.
// 设置 背景色以及标题颜色
if (getContainer() == null) {
mDecor.setWindowBackground(mBackgroundDrawable);
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;
}
- 代码1处,根据
feature
以及window
的类型 来确定引入的mContentParent
布局。一般全屏显示所布局为R.layout.screen_simple
。R.layout.screen_simple
中有个android:id="@android:id/content"
的FrameLayout
布局(不管layoutResource
对应的布局文件是那个,其都有android:id="@android:id/content"
的FrameLayout
)。 - 代码2处,调用
DecorView#onResourcesLoaded
,传入 layout 的 id,解析生成一个DecorView
的View对象属性mContentRoot
. - 代码3处,
findViewById
的实现在Window.java
中,其最终调用mDecor
的findViewById
方法,即搜索mDecor
下的对应id的View
.而ID_ANDROID_CONTENT
的值为com.android.internal.R.id.content
.显然,findViewById
获得的ViewGroup
对象就是mDecor
下的mContentRoot
下的 id为@android:id/content
的FrameLayout
对象。
接下来,就是代码分析了。
首先是R.layout.screen_simple
(/frameworks/base/core/res/res/layout/screen_simple.xml)布局代码,如下所示,这个没有可多介绍的。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
接下来看下 DecorView#onResourcesLoaded
代码。
class DecorView{
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
if (mBackdropFrameRenderer != null) {
...
}
//代码1
mDecorCaptionView = createDecorCaptionView(inflater);
//代码2处
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
//代码3
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
}
- 代码1 处创建了一个
mDecorCaptionView
对象,createDecorCaptionView
根据其英文注释是 自由浮动重叠窗口需要一个标题。(原文:Free floating overlapping windows require a caption.) 在Activity
的setContentView
流程中,mDecorCaptionView == null
,因此代码会走到代码3处 - 代码2 根据
layoutResource
,解析资源文件,生成对应的View
,即R.layout.screen_simple
(或其他)解析出来的View
。 - 代码3 将布局文件解析出来的
root
对象,添加为DecorView
的子View
再来看下PhoneView#generateLayout
方法中 findViewById
方法实现
class Window{
@Nullable
public View findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
}
显然 findViewById
就是从 mDecor
下寻找id
为R.id.content
的ViewGroup
对象。即PhoneView
的mContentParent
对象 就是 DecorView
下的mContentRoot
的下的一个 id
为content
的 子ViewGroup
.
再回到PhoneView#setContentView
方法,看代码2处代码
mLayoutInflater.inflate(layoutResID, mContentParent);
layoutResID
是Activity#setContentView
传入的layout
文件id值。显然就是将Activity#setContentView
传入的布局作为 mContentParent
的子View
。
看了这么多,我们对这些Activity
、PhoneWindow
、DecorView
能够汇总一个包涵关系了。
但是以上只是DecorView
的创建过程,此时View
还没被添加到Window
上去显示!!!
向 PhoneView 添加 DecorView
在 ActivityThread
的 handleLaunchActivity
中执行完 create 阶段的方法之后,立即开始调用 handleResumeActivity
方法,开始执行 resume
过程,handleResumeActivity
方法如下
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
//代码 1 间接调用 Activity的onResume
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//代码2 在 DecorView 添加 到 Window 之前,设置为不可见
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
// 代码3 设置 Window的类型,TYPE_BASE_APPLICATION 代表此 Window是一个应用级窗口,在该应用所有窗口最底下(如Dialog窗口就在 该窗口上面)
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 代码4 将decor 添加到 Window
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);
}
}
// 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;
}
...
}
代码4处调用了WindowManager
的 addView
方法。WindowManager
的实现类是WindowManagerImpl
,而WindowManagerImpl#addView
调用的是 WindowManagerGlobal
的addView
方法。
具体WindowManagerGlobal#addView
可以参考这篇文章Android 9和9之后系统 在onKeyDown中Dialog.show问题分析(以andoird 11 和android 9代码作为分析)