View的绘制流程

View的绘制流程分析

1、首先从Activty 的生命周期开始setContentVie()开始。在activity的setContentView 方法里面如:

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

调用的是phoneWindow.setContentView(@LayoutRes int layoutResID) 方法:

@Override
public void setContentView(int layoutResID) {
     // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
        // before this happens.
        //一开始mContactParent 是为null的,因此会先初始化DecorVIew
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        ....
      if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //将layout 解析并add 到mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }ontentParent.addView(view, params);
        }
        ...

主要关注installDecor(),

private void installDecor() {
     if (mDecor == null) {
            // new 一个Decorview 的对象
            mDecor = generateDecor(-1); //1
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
            .....
            if (mContentParent == null) {
            mContentParent = generateLayout(mDecor); //②

    

首先会先执行会上面的方法1, new 了一个Decorview的对象,而DecorView则是PhoneWindow类的一个内部类,继承于FrameLayout,由此可知它也是一个ViewGroup,那么DecorView起到什么作用呢?
其实DecorView 是viewTree里的顶层View,是继承FrameLayout 的,有标题view和内容view这两个子元素,而内容view则是上面提到的mContentParent。我们接着看②号代码获取对应的mConentParent对应的View .

protected ViewGroup generateLayout(DecorView decor) {
    ...
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...

其中ID_ANDROID_CONTENT = com.android.internal.R.id.content; 对应我们的内容view 中,这个view 就是用来填充我们之前setContantView 方法传过来的layout 对应的view.

现在会回到之前的phoneWindow.setContentView() 在installDecor 后会通过LayoutInflate.infalte 将传过来的layout解析成view 并添加到mContentParent 中。

2、接下来到Activity 的onResume()阶段。 android 都是消息驱动的, activity 的onResume 也是有消息驱动的,在ActiviAtyThread 收到onResume的消息会调用

ActivityThread 里面的handleResumeAcivity , 那么就到了acitivty 生命周期里的onResume 阶段 , 所以activity 里的view 渲染都是在onResume 阶段。

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        ...
        if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
        ...

在这个阶段会通过windowManager 将onCreate 创建的DecorView add 进去 。WindouwManager 的实现类是WindowManagerImpl如:

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

接下来调用到WindowManagerGlobal.addView 方法:

 public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
            
        ....
        
        root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
            
        ...
         try {
                root.setView(view, wparams, panelParentView);
            } 
        ...

在这里构造了ViewRootImpl类 .继续看root.setView 方法。

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

                mAttachInfo.mDisplayState = mDisplay.getState();
                mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
        
        ....
         // 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();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
        ....

接下来来调用了requestLaout 方法

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            if (ViewDebugManager.DEBUG_REQUESTLAYOUT) {
                Log.d(mTag, "requestLayout: mView = " + mView + ", this = " + this,
                        new Throwable("requestLayout"));
            }
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

在scheduleTraversals 里面将CALLBACK_TRAVERSAL 消息push (mChoreographer.postCallback) 到一个Choreographer的一个队列里面。

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            if (ViewDebugManager.DEBUG_SCHEDULETRAVERSALS) {
                Log.v(mTag, "scheduleTraversals: mTraversalBarrier = " + mTraversalBarrier
                        + ",this = " + this, new Throwable("scheduleTraversals"));
            }
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
        

在Choreographer 在将CALLBACK_TRAVERSAL 这种类型的数据放到队列的同时会通知Choreographer 的FrameDisplayEventReceiver 接受surfaceflinger 上传的Vsync 信号,收到后执行doFrame方法,最后会执行到TraversalRunnable 的run()方法,执行doTraversal 操作。

 final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
   void doTraversal() {
        if (ViewDebugManager.DEBUG_LIFECYCLE || ViewDebugManager.DEBUG_ENG) {
            Log.v(mTag, "doTraversal: mTraversalScheduled = " + mTraversalScheduled + " mFisrt = "
                  + mFirst + ",mTraversalBarrier = " + mTraversalBarrier + ",this = " + this);
        }

        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

doTraversal 主要调用了performTraversals()方法,在该方法里面分布调用performMeasure,performLayout,performDraw 分布完成view 的测量,布局,和绘制操作。

private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        ...
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        ...
        performLayout(lp, mWidth, mHeight);
        ...
        performDraw();
        ...

到了上面的流程后,就是对于的view 的测量,布局,和绘制操作流程分析了。 下一节分析ViewGroup的绘制流程

posted @ 2019-03-18 17:09  scoftlin  阅读(247)  评论(0)    收藏  举报