Android View绘制流程

Posted 瞌睡先生想睡觉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android View绘制流程相关的知识,希望对你有一定的参考价值。

或许你已经看到过很多博客总结的View的绘制流程的.我这篇博客是跟着源码去看,对自己学到的知识加以印证.水平有限,仅供参考,如有错误,欢迎指正

我在之前的博客就已经说明了Activity的启动到显示的相关流程,现在我们来看下View具体的工作流程.

上次我们说到root.setView(view, wparams, panelParentView);这段代码,并没继续深入这段代码去看View的工作流程,现在接着这段代码往下阅读



    /**
     * We have one child
     */
    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);

                mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
                mFallbackEventHandler.setView(view);
                mWindowAttributes.copyFrom(attrs);//保存LayoutParams参数
                if (mWindowAttributes.packageName == null) 
                    mWindowAttributes.packageName = mBasePackageName;
                
                attrs = mWindowAttributes;
                ......
				
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */

                // 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();//在这里会开始调用View的三大流程
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) 
                    mInputChannel = new InputChannel();
                
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try 
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);//通过WindowSession来完成Window添加的过程,这里不作赘述
                 catch (RemoteException e) 
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                 finally 
                    if (restore) 
                        attrs.restore();
                    
                
				
				......
            
        
    


    @Override
    public void requestLayout() 
        if (!mHandlingLayoutInLayoutRequest) 
            checkThread();//校验线程
            mLayoutRequested = true;
            scheduleTraversals();
        
    
	
    void checkThread() 
        if (mThread != Thread.currentThread()) 
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");//只有创建ViewRootImpl的线程才能触及它的视图
        
    
	
	
    void scheduleTraversals() 
        if (!mTraversalScheduled) 
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//内部Handler实现,执行mTraversalRunnable
            if (!mUnbufferedInputDispatch) 
                scheduleConsumeBatchedInput();
            
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        
    
	
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//实现Runnable接口
	
    final class TraversalRunnable implements Runnable 
        @Override
        public void run() 
            doTraversal();
        
    

    void doTraversal() 
        if (mTraversalScheduled) 
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) 
                Debug.startMethodTracing("ViewAncestor");
            

            performTraversals();//这个就是在其他博客上经常看到的方法,在这个方法里面里面会开始View的三大流程

            if (mProfile) 
                Debug.stopMethodTracing();
                mProfile = false;
            
        
    
	
	
	
	//这个方法里面有着大量的代码,我们省略和我们要看的无关主流程的代码
    private void performTraversals() 
        // cache mView since it is used so much below...
        final View host = mView;

        if (DBG) 
            System.out.println("======================================");
            System.out.println("performTraversals");
            host.debug();
        

        if (host == null || !mAdded)
            return;

		......
		
        final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
        if (mFirst || windowShouldResize || insetsChanged ||
                viewVisibilityChanged || params != null || mForceNextWindowRelayout) 
            mForceNextWindowRelayout = false;
			
			......

            try 
				
				......

                final boolean freeformResizing = (relayoutResult
                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
                final boolean dockedResizing = (relayoutResult
                        & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
                final boolean dragResizing = freeformResizing || dockedResizing;
                if (mDragResizing != dragResizing) 
                    if (dragResizing) 
                        mResizeMode = freeformResizing
                                ? RESIZE_MODE_FREEFORM
                                : RESIZE_MODE_DOCKED_DIVIDER;
                        startDragResizing(mPendingBackDropFrame,
                                mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
                                mPendingStableInsets, mResizeMode);
                     else 
                        // We shouldn't come here, but if we come we should end the resize.
                        endDragResizing();
                    
                
                if (!USE_MT_RENDERER) 
                    if (dragResizing) 
                        mCanvasOffsetX = mWinFrame.left;
                        mCanvasOffsetY = mWinFrame.top;
                     else 
                        mCanvasOffsetX = mCanvasOffsetY = 0;
                    
                
             catch (RemoteException e) 
            

            

            if (!mStopped || mReportNextDraw) 
                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                        updatedConfiguration) 
                    int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);//获取根布局的MeasureSpec,mWidth就是window的宽度,lp是之前在setView方法中保存的数据,是根视图的宽度
                    int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                    if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                            + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                            + " mHeight=" + mHeight
                            + " measuredHeight=" + host.getMeasuredHeight()
                            + " coveredInsetsChanged=" + contentInsetsChanged);

                     // Ask host how big it wants to be
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行performMeasure方法,开始Measure流程

                    // Implementation of weights from WindowManager.LayoutParams
                    // We just grow the dimensions as needed and re-measure if
                    // needs be
                    int width = host.getMeasuredWidth();
                    int height = host.getMeasuredHeight();
                    boolean measureAgain = false;

                    if (lp.horizontalWeight > 0.0f) 
                        width += (int) ((mWidth - width) * lp.horizontalWeight);
                        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                                MeasureSpec.EXACTLY);
                        measureAgain = true;
                    
                    if (lp.verticalWeight > 0.0f) 
                        height += (int) ((mHeight - height) * lp.verticalWeight);
                        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                                MeasureSpec.EXACTLY);
                        measureAgain = true;
                    

                    if (measureAgain) 
                        if (DEBUG_LAYOUT) Log.v(mTag,
                                "And hey let's measure once more: width=" + width
                                + " height=" + height);
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    

                    layoutRequested = true;
                
            
         else 
            // Not the first pass and no window/insets/visibility change but the window
            // may have moved and we need check that and if so to update the left and right
            // in the attach info. We translate only the window frame since on window move
            // the window manager tells us only for the new frame but the insets are the
            // same and we do not want to translate them more than once.
            maybeHandleWindowMove(frame);
        

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) 
            performLayout(lp, mWidth, mHeight);//Layout流程

            // By this point all views have been sized and positioned
            // We can compute the transparent area

            if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) 
                // start out transparent
                // TODO: AVOID THAT CALL BY CACHING THE RESULT?
                host.getLocationInWindow(mTmpLocation);
                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
                        mTmpLocation[0] + host.mRight - host.mLeft,
                        mTmpLocation[1] + host.mBottom - host.mTop);

                host.gatherTransparentRegion(mTransparentRegion);
                if (mTranslator != null) 
                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
                

                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) 
                    mPreviousTransparentRegion.set(mTransparentRegion);
                    mFullRedrawNeeded = true;
                    // reconfigure window manager
                    try 
                        mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
                     catch (RemoteException e) 
                    
                
            

            if (DBG) 
                System.out.println("======================================");
                System.out.println("performTraversals -- after setFrame");
                host.debug();
            
        

		......
		
        if (!cancelDraw && !newSurface) 
            if (mPendingTransitions != null && mPendingTransitions.size() > 0) 
                for (int i = 0; i < mPendingTransitions.size(); ++i) 
                    mPendingTransitions.get(i).startChangingAnimations();
                
                mPendingTransitions.clear();
            

            performDraw();//Draw流程
         else 
            if (isViewVisible) 
                // Try again
                scheduleTraversals();
             else if (mPendingTransitions != null && mPendingTransitions.size() > 0) 
                for (int i = 0; i < mPendingTransitions.size(); ++i) 
                    mPendingTransitions.get(i).endChangingAnimations();
                
                mPendingTransitions.clear();
            
        

        mIsInTraversal = false;
    

	//获取根布局的MeasureSpec
    private static int getRootMeasureSpec(int windowSize, int rootDimension) 
        int measureSpec;
        switch (rootDimension) 

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);//根视图的大小就是窗口的大小
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);//根视图的大小是根视图的最大值,且不能超过window的大小
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);//根视图的大小就是根视图本身的大小
            break;
        
        return measureSpec;
    
	
	

measure

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) 
        if (mView == null) 
            return;
        
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try 
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);//调用根视图的measure方法
         finally 
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        
    

	//View的measure方法,这个方法被final修饰不可进行重写,所以所有的View都是用的同一个measure方法
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) 
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) 
            Insets insets = getOpticalInsets();
            int oWidth  = insets.left + insets.right;
            int oHeight = insets.top  + insets.bottom;
            widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
            heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
        

        // Suppress sign extension for the low bytes
        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);

        final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;

        // Optimize layout by avoiding an extra EXACTLY pass when the view is
        // already measured as the correct size. In API 23 and below, thi

以上是关于Android View绘制流程的主要内容,如果未能解决你的问题,请参考以下文章

[译]Android view 测量布局和绘制的流程

反思|Android View机制设计与实现:测量流程

android view的 绘制流程

Android View 的测量流程详解

源码分析Android中View的绘制流程

源码分析Android中View的绘制流程