天天看点

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

UI 绘制流程及原理全过程解析

UI 绘制流程类文章在网上一搜一大把,但是都有一定的断层。比如只分析了View的三大流程 onMeasure 、 onLayout 、 onDraw ,但是这三个方法的调用链却没有描述。这篇文章就应运而生。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

Window 顶层 DecorView 身世

我们都知道,通过

startActivity

启动一个页面时,如果在

onCreate

没有调用

setContentView

,则当前页面是一个空白页面。我们通过 Layout Inspector 查看当前 View 层级如下图。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

发现最顶层的就是 DecorView。此时我们通过

setContentView

添加一个 TextView,在看一下 View 树如下图。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

通过对比可以看出我们的 TextView 是添加在了 id 为 content 的 ViewGroup 中。

但是我们没有添加这个 DecorView,秉着鲁迅的名言——存在即合理 的原则,所以 DecorView 就是系统帮我们添加了,下面我们就从

startActivity

入口去 Reading the fuck code。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

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 中,就梳理完成了。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

在来一张图总结一下这么个过程。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

到这里,交接仪式已经完成了。完美的从 Activity 启动交接到 View 的绘制了。下面在分析 View 的绘制相关。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

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 了。

最后我们将这三大流程来个图总结一下。

UI 绘制流程及原理全过程解析UI 绘制流程及原理全过程解析

ok,完美收官!

继续阅读