Android View体系 - 启动篇

Posted by SpirytusZ on 2022-03-12

前言

点击Launcher的应用图标,到应用的主页展示到屏幕的过程中,Android系统是如何将画面渲染出来的?

本文将分析应用启动时View相关的代码,理解启动期间View的相关代码,理解ViewWindowWindowManagerViewRootImplActivity之间的关系。

启动

我们都知道Android应用进程的入口是ActivityThread的main方法。在经过与system_server的一系列IPC交互后,最终AMS会发起EXECUTE_TRANSACTION的命令,启动category为android.intent.category.LAUNCHER的Activity,走到了ActivityThread.performLaunchActivity方法:

1
2
3
4
5
6
7
8
// ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
activity.attach(...);
...
mInstrumentation.callActivityOnCreate(...);
...
}

从源码可以看到,在应用启动时,会调用到Activity的attach方法,之后马上就会回调Activity的onCreate方法,看看attach内部做了什么:

1
2
3
4
5
6
7
8
9
10
11
// Activity.java
final void attach(...) {
...
mWindow = new PhoneWindow(...);
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
...
);
...
}

可以看到,调用到Activity的attach方法时,会为Activity创建一个Window,并为这个Window设置好WindowManager

设置的WindowManager是哪个类的实例?点进去看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Window.java
public void setWindowManager(WindowManager wm, ...) {
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

// WindowManagerImpl.java
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}

原来WindownManager是通过WindowManagerImplcreateLocalWindowManager创建而来的,而WindowManagerImpl的parentWindow,就是上面Activity在attach时创建的Window,即PhoneWIndow

此后,performLaunchActivity结束,Activity的状态置为ON_CREATE

AMS又再发起EXECUTE_TRANSACTION的命令,回调Activity的onStart方法,此时Activity开始可见:

1
2
3
4
5
6
// ActivityThread.java
public void handleStartActivity(...) {
...
updateVisibility(r, true /* show */);
...
}

至此,Activity开始可见,准备开始获取焦点。

获取焦点

AMS发起EXECUTE_TRANSACTION的命令,试图将Activity的生命周期扭转为RESUMED状态,于是代码就走到了ActivityThreadhandleResumeActivity方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ActivityThread.java
public void handleResumeActivity(...) {
...
if (r.window == null && !a.mFinished && willBeVisible) {
final Activity a = r.activity;
...
View decor = r.window.getDecorView();
...
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
...
if (!a.mWindowAdded) {
wm.addView(decor, l);
}
...
}
...
}

handleResumeActivity中,从启动的activity中获取DecorViewWindowManager之后,会将DecorView纳入WindowManager的管理。看看addView内部做了什么:

1
2
3
4
// WindowManagerImpl.java
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mParentWindow, ...);
}

mGlobal是WindowManagerGlobal,如其命名,是一个进程下的全局WindowManager,维护ViewWindow的关系。
看看WindowManagerGlobal的addView内部做了什么:

1
2
3
4
5
6
7
8
9
10
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, ...) {
...
ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
...
mViews.add(view);
mRoots.add(root);
...
root.setView(view, wparams, ...);
}

WindowManagerGlobal内部创建了一个ViewRootImpl实例,并把view,也就是DecorView给设置到ViewRootImpl里面去。setView十分重要,内部请求重新布局,并且Window开始获取焦点:

1
2
3
4
5
6
7
8
9
10
11
12
// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, ...) {
...
// 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();
...
// 获取焦点,最终回调DecorView的onWindowFocusChanged
mWindowSession.addToDisplayAsUser(...);
...
}

经过WindowSession的addToDispalyAsUser方法后,Window获取到了焦点,回调到DecorView的onWindowFocusChanged。在获取焦点前,有一个关键的requestLayout方法,内部Choregrapher请求VSync信号后,就开始的View的三大流程:测量、布局和绘制。先看看VSync信号的请求流程。

VSync信号

requestLayout十分重要,内部先开启Handler的同步屏障,而后Choregrapher开始等待vsync信号,信号到达,回调TraversalRunnable,开始执行View树的遍历:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// ViewRootImpl.java

public void requestLayout() {
...
scheduleTraversals();
...
}

void scheduleTraversals() {
...
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}

class TraversalRunnable implements Runnable {
public void run() {
doTraversal();
}
}

关键在postCallback,Choregrapher会在合适的时机回调TrasversalRunnable,会在什么时机?点进去看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Choregrapher.java
private void postCallbackDelayedInternal(int callbackType, ...) {
...
mCallbackQueues[callbackType].addCallbackLocked(...);
...
if (dueTime <= now) {
scheduleFrameLocked(now);
}
...
}

private void scheduleFrameLocked(long now) {
...
scheduleVsyncLocked();
...
}

private final FrameDisplayEventReceiver mDisplayEventReceiver;

private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}

private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { ... }

从代码可以看出,postCallback内部调用到了scheduleFrameLocked,请求VSync信号。mDisplayEventReceiver内部调用了native方法,在此不做赘述。

VSync信号到达,会回调到FrameDisplayEventReceiver的onVsync方法:

1
2
3
4
5
6
7
8
9
10
11
12
// Choregrapher.FrameDisplayEventReceiver.java
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
...
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}

public void run() {
...
doFrame(...);
}

VSync信号到达,FrameDisplayEventReceiver会发送一个带有callback的异步消息到Handler中,而后就会回调到FrameDisplayEventReceiver的run方法,执行Choregrapher的doFrame方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Choregrapher.java
void doFrame(...) {
...
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
...
}

void doCallback(int callbackType, long frameTimeNanos) {
...
callbacks = mCallbackQueues[callbackType] ...;
...
for (CallbackRecord c = callbacks; c != null; c = c.next) {
...
c.run(frameTimeNanos);
...
}
}

在doFrame内部调用到doCallbacks,doCallbacks内部Choregrapher会找到CALLBACK_TRAVERSAL类型的callback,并回调他的run方法,在CallbackRecord内部会调用到真正的callback,也就是前面传入的mTraversalRunnable

至此,开始调用ViewRootImpl的doTraversal方法,进一步调用performTraversal方法,正式进入View的三大流程: 测量、布局以及绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ViewRootImpl.java
void doTraversal() {
...
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
performTraversals();
...
}

private void performTraversals() {
...
performMeasure();
...
performLayout();
...
performDraw();
...
}

总结

从上面的分析可知,一个Activity对应一个Window,这个Window就是PhoneWindow,而其管理类便是WindowManager,对应WindowManagerImpl。这个WindowManagerImpl内部持有一个WindowManagerGlobal,统一管理当前应用进程的所有Window

Activity的Resume阶段,通过WindowManagerGlobal的addView将DecorViewViewRootImpl绑定在一起,形成一个View树,最终调用requestLayout,启动View(DecorView)的三大流程:测量、布局以及绘制。

在启动阶段的整体流程:

  1. Activity回调onCreate前,ActivityThread会为Activity创建一个Window,并为这个Window设置一个WindowManager;
  2. Activity回调onStart后,ActivityThread会把这个Activity设置为可见;
  3. Activity回调onResume后,会通过WindowManagerGlobal创建一个ViewRootImpl,此后通过ViewRootImpl请求第一次布局,在通过Choregrapher请求第一次VSync信号;之后收到VSync信号后,执行performTraversals,正式进入View的三大流程: 测量、布局以及绘制。

android-view-startup