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

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之后,最终会调用 InstrumentationexecStartActivity, 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,完美收官!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值