时序图预览
activity及其窗口显示流程图

activity的启动流程可参考:activity启动流程图
关键代码分析
ActivityThread 接收框架 (ActivityTaskManagerService ATMS) 回调,开始启动一个新的 Activity,这时候一般情况下新activity将会执行生命周期函数从 onCreate 到 onResume。这个过程大致可分为两部分,第一点是创建窗口 view,并实例化 activity 自身设置的布局。第二点是将属于这个 activity 的 view 树添加到 WMS 中,申请 surface,然后进行内容绘制并将窗口显示在屏幕上。首先看看第一部分,view 树实例化的过程:
ActivityThread
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions,
Intent customIntent) {
...
//初始化WindowManagerGlobal,进程单例,用于向WMS添加窗口view
WindowManagerGlobal.initialize();
//执行activity的实例化、attach
final Activity a = performLaunchActivity(r, customIntent);
...
return a;
}
private Activity performLaunchActivity(ActivityClientRecord r,
Intent customIntent) {
...
//创建activity context
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//反射实例化要启动的activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} catch (Exception e) {
}
try {
//进程application,一般已经初始化(进程启动阶段 bindApplication,
//回调handleLaunchActivity()之前)
Application app = r.packageInfo.makeApplicationInner(
false, mInstrumentation);
//记录activity实例
synchronized (mResourcesManager) {
mActivities.put(r.token, r);
}
// 初始化activity资源相关类加载器,必须和application context一致
appContext.getResources().addLoaders(
app.getResources().getLoaders().toArray(
new ResourcesLoader[0]));
appContext.setOuterContext(activity);
//初始化activity的成员变量,如window, FragmentController等
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.activityConfigCallback,
r.assistToken, r.shareableActivityToken);
...
//回调activity生命周期 onCreate()
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(
activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(
activity, r.state);
}
//activiy 处于onCreate阶段
r.setState(ON_CREATE);
} catch (SuperNotCalledException e) {
}
return activity;
}
Activity
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) {
//将activity context实例保存到父类成员变量中
attachBaseContext(context);
//初始化fragmentManager,使其持有activity回调,能与activity进行交互,
//实现相关生命周期的分发
mFragments.attachHost(null /*parent*/);
//创建activity的窗口(client)并初始化Window定义的一些回调,
//这些回调将会处理来自WMS的消息。PhoneWindow是Window的唯一继承类,其内部
//持有窗口view(DecorView)
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
//windowManager,用于将窗口view添加到WMS中
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
mWindowManager = mWindow.getWindowManager();
...
}
主线程执行 Activity#attach() 方法后,activity 相关的成员变量都差不多进行了初始化。我们比较关心的变量是 mWindow,它是一个 client 窗口的一个抽象,具体实现类是 PhoneWindow,其内部变量将持有窗口的顶级 view:DecorView。而目前仅仅是进行了窗口数据类型的实例化,并没有做相关 view 的实例化,view 树的创建会在 activity 的生命周期 onCreate 中进行,一般由应用调用 Activity#setContentView() 触发 inflate view 树。如果应用不调用这个方法,那么系统会在执行 activity 的 onResume 生命周期后将 activity 的窗口添加到 WMS 中时为 创建一个只有 DecorView 的 view 树,因此 activity 将会显示一个空白界面。下面看看 view 树的创建过程:
Activity
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//实例化一个DecorView,这个view是一个frameLayout,实现了一些Window
//回调,以及将窗口的输入事件进行了一个中转处理,使得如果没有子view
//消费输入事件时,activity能够对输入事件进行默认处理
installDecor();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//...
} else {
//将应用设置的布局文件进行实例化并作为一个子view添加到窗口view树中
//作为mContentParent的孩子
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//实例化DecorView,其作为窗口顶级view
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//根据应用设置的主题,选择一个布局模板,模板内部包括action bar等
//布局,用于显示应用(activity)的title。
//其中有一个ID为com.android.internal.R.id.content
//的ViewGroup,这个view就是应用自己的布局的父view
mContentParent = generateLayout(mDecor);
}
...
}
从以上代码可以知道,activity 的 onCreate 生命周期主要工作是通过调用 setContentView() 方法进行 view 树的创建。(BTW,如果应用的 activity 继承自 androidx 的 AppCompatActivity,虽然也是用过调用 setContentView() 方法来 inflate 应用自己的布局,但是流程上却不是直接调用 activity (Window)的 setContentView() 方法,因此上述流程并不会走,而是而是使用了一种代理模式,不再使用原生的布局模板,而是使用 AppCompat 的主题布局。初始化时机是一致的。)onCreate 生命周期的结束,activity 的初始 view 树也就创建完成了。这个时候 activity 只是创建一颗 view 树,但是该 view 树是不可见的,也没有进行任何的绘制,原因是 activity 的窗口 view 并没有添加到 WMS 中。
onCreate 结束后框架会回调 activity 的 onStart 生命周期。该生命周期比较简单:
ActivityThread
@Override
public void handleStartActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions,
ActivityOptions activityOptions) {
final Activity activity = r.activity;
...
//回调activity的onStart方法,逻辑相对简单
activity.performStart("handleStartActivity");
r.setState(ON_START);
//回调activity的onRestoreInstanceState(),恢复activity被销毁时保存值。
if (pendingActions.shouldRestoreInstanceState()) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(
activity, r.state, r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(
activity, r.state);
}
}
// Call postOnCreate()
if (pendingActions.shouldCallOnPostCreate()) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(
activity, r.state,r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(
activity, r.state);
}
}
//更新窗口的可见性,在activity第一次创建的时候该方法无效,原因是activity的
//mDecor成员还没有赋值。这个方法起作用的时机是activity onPause、onStop后
//重新回到前台,重走该生命周期时。为了保证窗口的可见性
updateVisibility(r, true /* show */);
mSomeActivitiesChanged = true;
}
该生命周期比较简单,从代码可以看出 activity#onRestoreInstanceState() 回调在 onStart 之后。
一般是由于 configuration change等 引起的 activity 重建才会回调该方法,其实 activity 在被销毁时保存的值可以在 onCreate 中恢复,但需要对参数 bundle 做判空处理,而这个方法一但被回调,说明参数 bundle 一定非空。
onStart 结束后,ActivityThread 将回调 activity#onResume 生命周期。这是 activity 显示的第二个阶段,也就是将窗口 view 添加到 WMS 中并触发 view 树的绘制,最终将内容显示在屏幕上:
ActivityThread
@Override
public void handleResumeActivity(ActivityClientRecord r,
boolean finalStateRequest, boolean isForward, String reason) {
...
//执行activity的onResume生命周期,如果这个activity不是首次创建
//(从onSTOP再次回到前台进行显示),那个这个方法会首先回调
//activity的onRestart、onStart方法然后才会执行onResume
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
...
final Activity a = r.activity;
...
// 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.
//该activity的窗口未添加到WMS(目前确实未添加),且activity不主动结束,也
//没有在这个阶段启动一个新的activity,那么将窗口添加到WMS中使其显示
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
willBeVisible = ActivityClient.getInstance()
.willActivityBeVisible(a.getActivityToken());
}
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();
...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//将decor添加到WM中
wm.addView(decor, 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) {
//该activity不显示的情况,不在本次分析情形中
r.hideForNow = true;
}
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
...
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
//确保activity窗口可见
r.activity.makeVisible();
}
}
...
}
WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
//弹窗情形,当前添加的窗口具有父窗口,需要根据父窗口调整一下当前
//添加窗口的布局参数
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
//没有父窗口的情形(比如activity类型的窗口),根据应用manifest文件
//的设置,决定要不要启用硬件加速绘制
}
}
//每个窗口view都有一个ViewRootImpl作为它的view parent,窗口内容的绘制,
//用户输入事件的分发及处理都是由ViewRootImpl主导的。
ViewRootImpl root;
synchronized (mLock) {
...
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
//如果decor已经被添加过,则马上将它删掉(停止动画等)
mRoots.get(index).doDie();
} else {
//同一个窗口view不能多次添加到WMS中
throw new IllegalStateException("View " + view
+ " has already been added to the "
+ "window manager.");
}
}
...
IWindowSession windowlessSession = null;
if (windowlessSession == null) {
//窗口新创建,是这种情形
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession);
}
view.setLayoutParams(wparams);
//分别将窗口view、viewRoot、窗口布局参数保存到对应的列表的同一个
//索引中
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, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView, int userId) {
synchronized (this) {
if (mView == null) {
//将窗口view(decor)设置为viewRoot的孩子
mView = view;
...
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.
//准备等待下一次vsync信号到来,进行第一次绘制,在将窗口添加到WMS
//之前申请绘制是为了确保进行第一帧绘制时,一定会重新布局窗口
requestLayout();
...
try {
...
//将窗口添加到WMS中
res = mWindowSession.addToDisplayAsUser(mWindow,
mWindowAttributes, getHostVisibility(),
mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(),
inputChannel, mTempInsets, mTempControls);
} catch (RemoteException e) {
...
//窗口添加失败...
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
//计算window frame,窗口的宽高、位置等信息
mWindowLayout.computeFrames(mWindowAttributes, state,
displayCutoutSafe, winConfig.getBounds(),
winConfig.getWindowingMode(),
UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
mInsetsController.getRequestedVisibilities(),
getAttachedWindowFrame(), 1f /* compactScale */,
mTmpFrames);
setFrame(mTmpFrames.frame);
...
//将ViewRootImpl设置为decor的view parent,后续view树申请
//重新布局,重新绘制等都会通过getParent()递归到viewroot触发新的
//绘制
view.assignParent(this);
...
// Set up the input pipeline.
//后面的代码是进行输入事件的处理的初始化
}
}
}
从以上 activity 的 onResume 生命周期已经 WindowManager#addView() 方法的关键代码分析可以知道,框架会首先回调 activity 的 onResume 生命周期,然后才会将窗口 view(decor)添加到 WMS 中并申请 app-vsync 信号进行 activity 界面第一帧的绘制。这也是为什么 activity 启动时需要执行完 onResume 生命周期之后才能看到界面的原因。
下面简单分析一下申请 app-vsync 信号触发 view 绘制流程大致流程:
ViewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
//每一帧的绘制只需申请一次,这使得一帧时间内,不同或相同子view可以
//多次申请绘制
mTraversalScheduled = true;
//向主线程message queue添加同步消息栅栏,这个栅栏插入后会使得在这个
//栅栏之后插入主线程的同步消息(runnable等)需要等到下一次vsync信号
//(也就是下一帧)到来,执行完绘制任务后才能够得到执行。而栅栏之前的
//同步消息不受它影响,可以正常被主线程处理。
mTraversalBarrier =
mHandler.getLooper().getQueue().postSyncBarrier();
//Choreographer本质是一个handler,所以申请下一次绘制的本质是往
//主线程message queue中添加一个异步消息,等待下一次屏幕刷新垂直信号
//(vsync)到来时执行这条消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL,
mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
//Choreographer#postCallback() 方法最终会调用一个native 函数,请求接收一个
//屏幕刷新信号
DisplayEventReceiver
/**
* Schedules a single vertical sync pulse to be delivered when the
* next display frame begins.
*/
@UnsupportedAppUsage
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but"
+ "the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
窗口申请绘制的 ViewRootImpl#scheduleTraversals() 方法流程比较简单,首先往主线程message queue 插入一个同步消息栅栏降低后续主线程繁忙的可能性,然后向消息队列发送一条异步消息,这条消息处理时就会执行窗口绘制的逻辑。最后调用 native 方法向 SurfaceFlinger 申请接收下一次屏幕刷新的垂直信号。在下一个 app-vsync 到来时主线程执行异步消息,最终调用 ViewRootImpl#performTraversals() 方法进行窗口一帧的绘制。这一帧绘制结束提交后将会被 SurfaceFlinger 用来进行合成,最终显示在屏幕上。
总结:activity 从启动开始执行相关生命周期函数后到其内容显示在屏幕上并开始响应用户输入实现交互的过程主要分为两部分:
- 首先执行 onCreate 生命周期函数,在这个方法中主要任务是完成 activity (窗口)view 树的创建,此时 activity 的内容并不可见,原因是其窗口 view 并没有添加到 WMS 中。
- 连续执行 onStart 和 onResume 生命周期函数,在 onResume 方法执行结束后框架会将该窗口的添加到 WMS 中,这个过程会初始化窗口对应的 ViewRootImpl,申请绘制,初始化窗口接收用户输入事件的通道等。从此 activity 内容可见并可以开始与用户进行交互。
本文详细分析了Android Activity的启动流程,从时序图预览到关键代码分析,重点探讨了从onCreate到onResume期间Activity窗口的创建、view树的构建以及如何显示在屏幕上。在onCreate中,通过setContentView()方法初始化view树,而在onResume后,窗口view被添加到WMS并申请绘制,最终在屏幕上显示。

7956

被折叠的 条评论
为什么被折叠?



