从 Activtity 启动到 View 绘制

时序图

Activtity 的第一次绘制

ActivityThread Activtity Instrumentation WindowManagerImp WindowMan ViewRootImpl 1,H ->> handleResumeActivity 2,performResumeActivity 3,r.activity.performResume(r.startsNotResumed, reason) 4,mInstrumentation.callActivityOnResume 5,onResume LayoutParams l = r.window.getAttributes() 6,wm.addView(decor, l) 7,mGlobal.addView(view, params, ... , ...) 8,root = new ViewRootImpl(view.getContext(), display) 9,root.setView(view, wparams, panelParentView) 10,requestLayout // 绘制流程 ActivityThread Activtity Instrumentation WindowManagerImp WindowMan ViewRootImpl

Activity 的 onCreate 中会通过 setContentView 函数添加自定义的布局文件到 Activity 所关联的 ViewTree,完成构造 ViewTree ;

然后,在 Activity 的 onResume 函数以后,开始绘制 Activity 所关联的 ViewTree;


绘制工作的三大流程

每一次绘制都是 post 一个任务让 handler 处理:

WindowMan ViewRootImpl Choreographer TraversalRunnable DecorView 11,root.setView(view, wparams, panelParentView) requestLayout 13,scheduleTraversals mChoreographer.postCallback( ... , mTraversalRunnable , ...) run doTraversal 17,performTraversals 18,performMeasure measure 19,performLayout layout 20,performDraw draw drawSoftware draw WindowMan ViewRootImpl Choreographer TraversalRunnable DecorView

ViewRootImpl.java 源码

查源码的链接

简单源码

Activtity 的第一次绘制

  • ActivityThread#handleResumeActivity
	View decor = r.window.getDecorView();
	// 略。。。
	ViewManager wm = a.getWindowManager();
	WindowManager.LayoutParams l = r.window.getAttributes();
	// 略。。。
	wm.addView(decor, l);
  • WindowManagerImpl#addView
  • WindowManagerGlobal#addView
	root = new ViewRootImpl(view.getContext(), display);
	// 略。。。
	try {
	       root.setView(view, wparams, panelParentView);
    }

一个 Activity 对应 一个 window,一个 DecorView 和 一个 ViewRootImpl;

  • ViewRootImpl#setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
      synchronized (this) {
	if (mView == null) {
	    mView = view;

	    mWindowAttributes.copyFrom(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();
  • ViewRootImpl#requestLayout
	// 开始 View 的绘制工作流程

### 绘制工作的三大流程
  • ViewRootImpl#requestLayout
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
  • ViewRootImpl#scheduleTraversals
    mChoreographer = Choreographer.getInstance();

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable/*接着这看*/, null);

            ......
        }
    }
  • ViewRootImpl#mTraversalRunnable
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
  • ViewRootImpl#doTraversal
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            ...

            performTraversals();// 此处触发三大流程 
	  
            ...
        }
    }
  • ViewRootImpl#performTraversals
	boolean newSurface = false;
	WindowManager.LayoutParams lp = mWindowAttributes;
	Rect frame = mWinFrame;

	if (mWidth != frame.width() || mHeight != frame.height()) {
		mWidth = frame.width();
		mHeight = frame.height();
	}
	int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
	int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

	// ---------------------- 分割线 ----------------------

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        if (didLayout) {
            performLayout(lp, mWidth, mHeight);

	// ---------------------- 分割线 ----------------------

                if (!hadSurface) {
                    if (mSurface.isValid()) {
                        newSurface = true;

	// 略。。。
        if (!cancelDraw && !newSurface) {
            performDraw();

  • ViewRootImpl#performMeasure
	mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  • ViewRootImpl#performLayout
	final View host = mView;

	host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

	mInLayout = false;
	int numViewsRequestingLayout = mLayoutRequesters.size();
	if (numViewsRequestingLayout > 0) {
		 // 。。。
	    ArrayList<View> validLayoutRequesters = getValidLayoutRequesters (mLayoutRequesters,false);

	     if (validLayoutRequesters != null) {
	       // 。。。
	       mInLayout = true;
	       host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
	       // 。。。
  • ViewRootImpl#performDraw
  • ViewRootImpl#draw
  • ViewRootImpl#drawSoftware
	mView.draw(canvas);

拓展

DecorView 的测量规格

在 ViewRootImpl#performTraversals 函数中的

WindowManager.LayoutParams lp = mWindowAttributes;

决定了 DecorView 的布局规格,那么这个 mWindowAttributes 是在哪里决定了呢?

经过时序图,可以追踪到 ActiviyThread 的 handleResumeActivity 函数中:

    WindowManager.LayoutParams l = r.window.getAttributes();

r 就是 ActivityClientRecord( ActivityThread 的内部类 ),其 window 字段的赋值:

    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
            
        r.window = r.activity.getWindow();
// Activity.java
    private Window mWindow;
    
    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) {
            
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        
        ...
        
    }
    
    public Window getWindow() {
        return mWindow;
    }

其实,r.window.getAttributes() 最终调用到的是 PhoneWindow 的 父类 Window 的 getAttributes() 函数;

// Window.java

    // The current window attributes.
    private final WindowManager.LayoutParams mWindowAttributes =
        new WindowManager.LayoutParams();
        
    public void setAttributes(WindowManager.LayoutParams a) {
        mWindowAttributes.copyFrom(a);
        dispatchWindowAttributesChanged(mWindowAttributes);
    }
    
    public final WindowManager.LayoutParams getAttributes() {
        return mWindowAttributes;
    }
// .../Android/sdk/sources/android-28/android/view/
// WindowManager.java

public interface WindowManager extends ViewManager {

    ...
    
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {

        ...
        
        public LayoutParams() {
            super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            type = TYPE_APPLICATION;
            format = PixelFormat.OPAQUE;
        }
        ...
    }
    ...
}
// .../Android/sdk/sources/android-28/android/view/
// ViewGroup.java

public static class LayoutParams {
        ...
        public LayoutParams(int width, int height) {
            this.width = width;
            this.height = height;
        }
        ...
}

所以,DecorView 所在的 window 的宽高规格就是:

LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT;

根据 getRootMeasureSpec 函数的逻辑:

MATCH_PARENTWRAP_CONTENTdefault
modeEXACTLYAT_MOSTEXACTLY
sizewindowSizewindowSizerootDimension

可知,DecorView 的宽高的初始规格就是 EXACTLY & windowSize


Activity 与 Window 与 View 之间的关系

参考:一篇文章看明白 Activity 与 Window 与 View 之间的关系

引用图片:

onCreate() - Window 创建过程:

onCreate() - Window 创建过程

onResume() - Window 显示过程:

onResume() - Window 显示过程

Activity 中 Window 创建过程:

Activity 中 Window 创建过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值