UI 绘制流程及原理全过程解析
UI 绘制流程类文章在网上一搜一大把,但是都有一定的断层。比如只分析了View的三大流程 onMeasure 、 onLayout 、 onDraw ,但是这三个方法的调用链却没有描述。这篇文章就应运而生。
Window 顶层 DecorView 身世
我们都知道,通过 startActivity
启动一个页面时,如果在 onCreate
没有调用 setContentView
,则当前页面是一个空白页面。我们通过 Layout Inspector 查看当前 View 层级如下图。
发现最顶层的就是 DecorView。此时我们通过 setContentView
添加一个 TextView,在看一下 View 树如下图。
通过对比可以看出我们的 TextView 是添加在了 id 为 content 的 ViewGroup 中。
但是我们没有添加这个 DecorView,秉着鲁迅的名言——存在即合理 的原则,所以 DecorView 就是系统帮我们添加了,下面我们就从 startActivity
入口去 Reading the fuck code。
Activity 启动流程之 PhoneWindow
当我们调用 startActivity
之后,最终会调用 Instrumentation
的 execStartActivity
, Instrumentation 主要负责 Activity 相关生命周期函数的调用。
//Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
......
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
......
}
//Instrumentation.java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
......
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
......
return null;
}
//ActivityManagerService.java
public int startActivity(IBinder whoThread, String callingPackage,
Intent intent, String resolvedType, Bundle bOptions) {
......
return mActivityStarter.startActivityMayWait(appThread, -1, callingPackage, intent,
resolvedType, null, null, null, null, 0, 0, null, null,
null, bOptions, false, callingUser, null, tr);
}
而 AMS 的 startActivity 最终会通过 ActivityStackSupervisor 调用 ActivityThread 的 scheduleLaunchActivity。在 handle 中调用了 handleLaunchActivity 处理 Activity 的启动事件。
//ActivityThread.java
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
......
sendMessage(H.LAUNCH_ACTIVITY, r);
......
}
private class H extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
......
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
......
}
}
}
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
......
Activity a = performLaunchActivity(r, customIntent);
......
//performLaunchActivity 流程执行完 ,在来说handleResumeActivity。
handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
......
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
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);
......
}
在 Activity 的 attach 方法中创建了一个 PhoneWindow 对象。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback {
private Window mWindow;
final void attach(...){
......
mWindow = new PhoneWindow(this, window);
......
}
}
可以看到 PhoneWindow 是 Window 的一个实现类
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*
* 解释:每一个 Activity 都持有一个 Window 对象,但是 Window 是一个抽象类,
* 这里 Android 为 Window 提供了唯一的实现类 PhoneWindow。
* 也就是说 Activity 中的 window 实例就是一个 PhoneWindow 对象。
*/
public abstract class Window {
}
在 PhoneWindow 中看到了我们熟悉的 DecorView。在 Activity 的 attach 方法中会调用如下构造函数初始化 PhoneWindow 的同时生成了一个 DecorView。
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// This is the top-level view of the window, containing the window decor.
//这是窗口的顶层视图。
private DecorView mDecor;
public PhoneWindow(Context context, Window preservedWindow) {
......
mDecor = (DecorView) preservedWindow.getDecorView();
......
}
}
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
private void installDecor() {
......
mDecor = generateDecor(-1);
......
mContentParent = generateLayout(mDecor);
......
}
protected DecorView generateDecor(int featureId) {
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
protected ViewGroup generateLayout(DecorView decor) {
......
layoutResource = R.layout.screen_simple;
......
//负责将 screen_simple 布局文件添加到 decor 中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
......
}
<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>
最后会在 handleResumeActivity 将我们创建的 DecorView 添加到 Window中。
final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
......
//该 wm 是 WindowManager 的实现类 WindowManagerImpl
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
......
}
至此,我们就从 Activity 启动,到 DecorView 创建,再到系统默认布局文件添加到 DecorView 中,就梳理完成了。
在来一张图总结一下这么个过程。
到这里,交接仪式已经完成了。完美的从 Activity 启动交接到 View 的绘制了。下面在分析 View 的绘制相关。
View 绘制流程
上面我们追踪到了 WindowManagerImpl 的 addView 方法。然后会执行 ViewRootImpl 的 setView 方法。
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
......
ViewRootImpl root;
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mRoots.add(root);
root.setView(view, wparams, panelParentView);
......
}
单独看 ViewRootImpl 名字,就能看出他是所有 View 的根节点的一个实现类。在看看 setView 方法中做了什么事。
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks {
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......
requestLayout();
......
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//这里会通过 Choreographer 编舞者 执行一个runnable,
//最终执行 doTraversal() 方法
scheduleTraversals();
}
}
void doTraversal() {
......
performTraversals();
......
}
private void performTraversals() {
......
//Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, mWidth, mHeight);
......
performDraw();
......
}
}
到这里是不是就很熟悉了,我们常见的 View 三大流程 layout 、 measure 、 draw 了。
最后我们将这三大流程来个图总结一下。
ok,完美收官!