点击阅读:Activity 启动流程系列文章
点击阅读:Activity 启动流程系列文章
阅读本文前,如果不了解 Activity、Window 和 View 之间的关系,可以先阅读:Android 窗口显示(一)------ Activity、Window 和 View 之间的联系
本文中将以 Activity 生命周期为时间线,分析 Activity 的窗口显示流程。
1. Activity onCreate 回调执行前
performLaunchActivity 方法中,在 onCreate 方法回调执行前,做了以下几件事:
- 反射创建 Activity 实例
- 调用 Activity attach 方法关联上下文
- 初始化 PhoneWindow
- 调用 onCreate 方法
1.1 Activity 实例创建与初始化
看一下 Activity 启动过程中的 performLaunchActivity 方法:
java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
if (activity != null) {
Window window = null;
activity.attach(activityBaseContext, 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.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
}
...
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
1.2 attach 关联上下文
此方法中通过反射创建了 Activity 实例,接着调用 attach 方法为 Activity 关联上下文环境:
java
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,
IBinder shareableActivityToken) {
...
// 创建 PhoneWindow 对象,PhoneWindow 初始化时创建了 DecorView 对象
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
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);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
...
// PhoneWindow set WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
// mWindowManager 转为 WindowManagerImpl 对象,WindowManagerImpl 为 WindowManager 的实现类
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
mWindow.setPreferMinimalPostProcessing(
(info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);
getAutofillClientController().onActivityAttached(application);
setContentCaptureOptions(application.getContentCaptureOptions());
}
1.3 初始化 PhoneWindow
在 attach 方法中创建了 PhoneWindow 对象,这里可以看出来每一个 Activity 都会对应一个 PhoneWindow。因为传入的 preservedWindow 为 null,所以不走大括号里的逻辑。
每个 Activity 都会创建一个 Window 用于承载 View 视图的显示,Window 是一个抽象类,PhoneWindow 是其唯一的实现类。看一下 PhoneWindow 的构造方法:
java
public PhoneWindow(@UiContext 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) {
// 创建 DecorView
mDecor = (com.android.internal.policy.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;
// 通过 DecorView 获取 ViewRootImpl 实例
final ViewRootImpl viewRoot = mDecor.getViewRootImpl();
if (viewRoot != null) {
// Clear the old callbacks and attach to the new window.
viewRoot.getOnBackInvokedDispatcher().clear();
onViewRootImplSet(viewRoot);
}
}
// 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;
}
1.4 初始化 WindowManagerImpl
继续看 attach 方法,调用 PhoneWindow 的 setWindowManager 方法,为 PhoneWindow 添加一个 WindowManager 实例,实际上是 WindowManagerImpl 对象:
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);
}
// WindowManagerImpl
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
WindowManagerImpl 作为应用层面的接口实现类,将应用层对窗口的操作请求转发给系统层的 WindowManagerGlobal 进行处理:
java
public final class WindowManagerImpl implements android.view.WindowManager {
2. Activity onCreate 回调执行中
Activity onCreate 回调执行中,做了以下几件事:
- setContentView 流程
- 创建 DecorView 对象
2.1 调用 Activity onCreate
运行完 Activity 的 attach 方法后,回到 performLaunchActivity 方法中,会调用 Instrumentation 的 callActivityOnCreate 方法执行 Activity onCreate() 方法:
java
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
java
final void performCreate(Bundle icicle) {
performCreate(icicle, null);
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
...
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
}
2.2 主动调用 setContentView
我们在实现 Activity 的时候都会在 onCreate 方法中调用 setContentView 方法传入布局。并且一般我们的 Activity 都会继承自 AppCompatActivity。
看一下 AppCompatActivity 的 setContentView 方法:
java
@Override
public void setContentView(View view) {
initViewTreeOwners();
getDelegate().setContentView(view);
}
接着调用了 AppCompatDelegateImpl 的 setContentView 方法:
java
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.bypassOnContentChanged(mWindow.getCallback());
}
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
}
}
private ViewGroup createSubDecor() {
mWindow.getDecorView();
}
2.3 创建 DecorView 对象
接着调用了 PhoneWindow 的 getDecorView 方法:
java
@Override
public final @NonNull View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
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);
}
...
}
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);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
Activity 首次调用 setContentView 时,会通过 installDecor 方法创建了一个 DecorView 对象,最终 getDecorView 将该对象返回。
通过 getDecorView 方法创建了 DecorView:
java
@Override
public final @NonNull View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
通过 installDecor 初始化 DecorView 以及其内部的 content:
java
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 创建 DecorView 实例,初始化 DecorView,是Activity的根View
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) {
// 返回 DecorView 中的 contentView
mContentParent = generateLayout(mDecor);
...
}
}
3. Activity onResume 回调执行后
在 Activity 的 onResume 生命周期回调执行完成后,WindowManagerImpl 的 addView 方法执行完成后,Activity 才会显示到屏幕上了。
Activity onResume 回调执行后,做了以下几件事:
- 构建布局参数
- 调用 WindowManagerGlobal 的 addView 方法
- 创建 ViewRootImpl 对象,调用 setView 方法
3.1 构建布局参数
接着看一下 ActivityThread 的 handleResumeActivity 方法:
java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
...
// performResumeActivity 方法内部会走 onResume
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
...
// r 为 ActivityClientRecord, ActivityClientRecord 中的 window 属性为 null,并且 Activity 没有完成且即将可见
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();// PhoneWindow
View decor = r.window.getDecorView();// 通过 PhoneWindow 获取到 DecorView
decor.setVisibility(View.INVISIBLE);// DecorView 设置为不可见
// WindowManagerImpl 实现了 ViewManager 接口
ViewManager wm = a.getWindowManager();// 获取到 WindowManagerImpl 对象,在 Activity attach 中赋值给 mWindowManager
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;// 将 DecorView 与 Activity 关联起来
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();
}
}
// 如果 Activity 可见
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// WindowManagerImpl addView(DecorView, WindowManager.LayoutParams)
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.
}
}
3.2 WindowManagerImpl addView
构建好了布局参数 WindowManager.LayoutParams l
之后,调用了 WindowManagerImpl 的 addView 方法:
java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyTokens(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
3.3 创建 ViewRootImpl 对象,调用 setView 方法
接着调用了 WindowManagerGlobal 的 addView 方法:
java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, android.view.Window parentWindow, int userId) {
...
ViewRootImpl root;
View panelParentView = null;
...
// 创建 ViewRootImpl 对象,一个 Activity 对应一个 Window 对应一个 ViewRootImpl。这句话不一定对,因为 ArrayList<ViewRootImpl> mRoots
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession, new WindowlessWindowLayout());
}
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 {
// DecorView
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
// BadTokenException or InvalidDisplayException, clean up.
if (viewIndex >= 0) {
removeViewLocked(viewIndex, true);
}
throw e;
}
}
}
最终调用了 ViewRootImpl 的 setView 方,在 Activity 的 onResume 生命周期回调执行完成后,WindowManagerImpl 的 addView 方法执行完成后,Activity 才会显示到屏幕上了。