View体系6:ViewTree的遍历流程

0 概述

UI显示的3要素是:尺寸大小,位置,内容,在便利过程中分别对应以下三个函数

  • performMeasure:大小
  • performLayout :位置
  • performDraw:绘制

performTraversal的实现主体

performTraversals{
    if(needReMeasure){
        performMeasure
    }
    if(needRelayout){
        performLayout
    }
    if(needReDraw){
        performDraw
    }
} 

1.performTraversals中涉及的部分重要变量

  • mView/host:mView是在ViewRootImpl管理下ViewTree的根节点,函数在执行过程中需要频繁使用这个变量,为了避免意外的篡改,performTraversals函数开头使用了一个本地变量host来指向它,后面的操作全部由这个变量来完成。

  • mFirst:判断是否是第一次执行performTraversals

  • mIsInTraversal:当前是否在执行Traversal

  • mFullRedrawNeeded:某些情况下需要重绘所有界面,这个变量将影响到后续的performDraw

  • mLayoutRequested:很多情况下,该变量都会被置为true,如View主动发起一个requestLayout,第一次调用performTraversals方法,当前的宽高和期望值不符

  • mWinFrame:由WMS计算得到的Frame

  • mFitSystemWindowInsets:设置内边距的初始值,以容纳包括状态栏输入法在内的系统窗口

  • mAttachInfo:当View被attached到window时,用于记录一系列相关信息,

  • mWidth/mHeight:通过主动请求WMS计算后得到的值,要注意和mDesiredWidth/mDesiredHeight的区别,mDesired这组值和mWinFrame保持一致,表示WMS期望的应用程序的宽和高

  • sWindowSession:ViewRoot与WMS的通信接口

2.三大步骤

2.1 performMeasure

private void performTraversals() { 
    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null) { 
        //层级1 
        if (!mStopped) {//层级2
            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                    (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
            if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                    || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
                    //层级3
                int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                 // Ask host how big it wants to be
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {

    case ViewGroup.LayoutParams.MATCH_PARENT: 
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    case ViewGroup.LayoutParams.WRAP_CONTENT: 
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    default: 
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
} 

层级1:
mFirst:第一次调用performTraversals
windowShouldResize:windowSize确定需要改变
viewVisibilityChanged:ViewRootImpl记录的Visibility和ViewTree根节点所记录的值产生差异时,说明可见性有变化;或者需要为这个窗口产生一个Surface,那么这个变量为true。
params:ViewTree的LayoutParams,应用程序可以通过多种方法来设置。

层级2:
mStopped:当Activity处于stop状态时,其对应的Window也同样会被stopped
层级3:
focusChangedDueToTouchMode:TouchMode是否会引起foucus变化
mWdith!=host.getMeasuredWidth():getMeasuredWidth是View经过onMeasure测量出来的宽度
mHeight != host.getMeasuredHeight():getMeasuredHeight是View经过onMeasure后测量出来的高度
contentInsetsChanged:

2.2 performMeasure

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { 
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        
    }
} 

ViewRootImpl将控制权转给View树的根元素,真正的Traversal开始。真正的测量工作也是在onMeasure执行

2.3 onMeasure

widthMeasureSpec,heightMeasureSpec是该View对象的上一级父对象要求的宽高,层层而下传递到ViewTree中的各个元素,ViewTree的根元素mDecor的这两个值是由ViewRootImpl传入的,后者通过MeasureSpec.makeMeasureSpec来生成符合要求的spec

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    //measure调用onMeasur
    onMeasure(widthMeasureSpec, heightMeasureSpec); 
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
} 

2.4Spec的格式如下

mode(高二位)+size

2.4.1 mode有三种

UNSPECIFIED=0;父对象没有强制要求子对象必须遵循哪些约束
EXACTLY:父对象要求子对象必须严格按照它给定的值来约束自己
AT_MOST:子对象可以自行选择给定范围内的值

2.5 FrameLayout的onMeasure分析

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();//子对象个数
    //判断父对象的mode要求
    final boolean measureMatchParentChildren =
            MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
            MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
    mMatchParentChildren.clear();
    //所有子对象中测量到的最大高度和宽度
    int maxHeight = 0;
    int maxWidth = 0;
    int childState = 0;

    for (int i = 0; i < count; i++) {//循环处理所有子对象
        final View child = getChildAt(i);//获取一个子对象
        if (mMeasureAllChildren || child.getVisibility() != GONE) {//需要测量吗?
            //step2:考虑padding和margin生成新的spec
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            //step3 获取最大值
            maxWidth = Math.max(maxWidth,
                    child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
            maxHeight = Math.max(maxHeight,
                    child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
            childState = combineMeasuredStates(childState, child.getMeasuredState());
           
        }
    }
    //step4 综合考虑其他因素
    // 检查padding
    maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
    maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

    // 检查建议的最小宽高值
    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    // 检查foreground北京宽高值
    final Drawable drawable = getForeground();
    if (drawable != null) {
        maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
        maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }
    //记录结果
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            resolveSizeAndState(maxHeight, heightMeasureSpec,
                    childState << MEASURED_HEIGHT_STATE_SHIFT));

    count = mMatchParentChildren.size();
    if (count > 1) {
        for (int i = 0; i < count; i++) {
            final View child = mMatchParentChildren.get(i);

            final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            int childWidthMeasureSpec;
            int childHeightMeasureSpec;
            
            if (lp.width == LayoutParams.MATCH_PARENT) {
                childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
                        getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
                        lp.leftMargin - lp.rightMargin,
                        MeasureSpec.EXACTLY);
            } else {
                childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                        getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                        lp.leftMargin + lp.rightMargin,
                        lp.width);
            }
            
            if (lp.height == LayoutParams.MATCH_PARENT) {
                childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
                        getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
                        lp.topMargin - lp.bottomMargin,
                        MeasureSpec.EXACTLY);
            } else {
                childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                        getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                        lp.topMargin + lp.bottomMargin,
                        lp.height);
            }

            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
    }
}
 

View只有padding,ViewGroup中各个子view之间是margin##

2.2 performLayout

performTraversals(){
    final boolean didLayout = layoutRequested && !mStopped;
    if (didLayout) {
        performLayout(lp, desiredWindowWidth, desiredWindowHeight);
} 

2.2.1 ViewRootImpl的performLayout

private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
        int desiredWindowHeight) { 
    final View host = mView; 
    try {
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        //......
    }
} 

2.2.2 View的layout方法

l,t,r,b表示该View对象与父对象边框的距离,如果changed为true,表名本次设置的边距与上一次相比发生了变化,或者flags强制要求layout,那么就调用onLayout。View中onLayout是空的,ViewGroup需要重载ViewGroup的onLayout方法

public void layout(int l, int t, int r, int b) {
  
    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b); 
    }
    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
}

2.2.3 FrameLayout的onLayout方法

protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    final int count = getChildCount(); 
    final int parentLeft = getPaddingLeftWithForeground();
    final int parentRight = right - left - getPaddingRightWithForeground();

    final int parentTop = getPaddingTopWithForeground();
    final int parentBottom = bottom - top - getPaddingBottomWithForeground();

    mForegroundBoundsChanged = true; 
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() != GONE) {
            //child设置的layout属性
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            //child在measure中测量到的宽度和高度
            final int width = child.getMeasuredWidth();
            final int height = child.getMeasuredHeight(); 
            int gravity = lp.gravity;  
            final int layoutDirection = getLayoutDirection();
            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
            final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
            //childLeft和childTop的计算
            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                case Gravity.LEFT:
                    childLeft = parentLeft + lp.leftMargin;
                    break;
                case Gravity.CENTER_HORIZONTAL:
                    childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                    lp.leftMargin - lp.rightMargin;
                    break;
                case Gravity.RIGHT:
                    childLeft = parentRight - width - lp.rightMargin;
                    break;
                default://NOTICE
                    childLeft = parentLeft + lp.leftMargin;
            }

            switch (verticalGravity) {
                case Gravity.TOP:
                    childTop = parentTop + lp.topMargin;
                    break;
                case Gravity.CENTER_VERTICAL:
                    childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                    lp.topMargin - lp.bottomMargin;
                    break;
                case Gravity.BOTTOM:
                    childTop = parentBottom - height - lp.bottomMargin;
                    break;
                default:
                    childTop = parentTop + lp.topMargin;
            } 
            child.layout(childLeft, childTop, childLeft + width, childTop + height);
        }
    }
}

TU 11-13 childLeft计算过程中涉及到的变量解释

2.3 performDraw

对象的layout确定之后,它才能在此基础上draw,函数performDraw是遍历流程中最后被调用的,将在画板上产生UI数据,然后在适当的时机由SurfaceFlinger进行整合,最终显示到屏幕上,绘制UI的核心如下:

  • Surface:Surface是画板
  • 绘制图形的方式:软件绘制 或 硬件绘制
  • ViewTree各个元素的协调关系

2.3.1 drawSoftWare

 private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
        boolean scalingRequired, Rect dirty) {

    // Draw with software renderer.
    Canvas canvas;
    try {
        //区域
        int left = dirty.left;
        int top = dirty.top;
        int right = dirty.right;
        int bottom = dirty.bottom;
        //锁定画布
        canvas = mSurface.lockCanvas(dirty); 
        canvas.setDensity(mDensity);
    } catch (Surface.OutOfResourcesException e) {
       
    } catch (IllegalArgumentException e) { 
    }

    try {
 
        if (!canvas.isOpaque() || yoff != 0) {
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }

        dirty.setEmpty();
        mIsAnimating = false;
        attachInfo.mDrawingTime = SystemClock.uptimeMillis();
        mView.mPrivateFlags |= View.PFLAG_DRAWN;
 
        try {
            canvas.translate(0, -yoff);
            if (mTranslator != null) {
                mTranslator.translateCanvas(canvas);
            }
            canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
            attachInfo.mSetIgnoreDirtyState = false;

            mView.draw(canvas);//真正的画画操作
            //draw完成后的Canvas信息通过Surface提交给SurfaceFlinger,
            //并由SurfaceFlinger统一合成渲染到FrameBuffer中,才能最终把界面显示到屏幕
            drawAccessibilityFocusedDrawableIfNeeded(canvas);
        } finally { 
        }
    } finally {
        try {
            surface.unlockCanvasAndPost(canvas);
        } catch (IllegalArgumentException e) { 
        } 
    }
    return true;
}

AIDL


参考
http://www.infoq.com/cn/news/2015/09/android-weekly-react-native
http://blog.csdn.net/lmj623565791/article/details/38461079
http://blog.csdn.net/lmj623565791/article/details/47017485
http://www.infoq.com/cn/news/2015/07/android-weekly-m
http://www.race604.com/communicate-with-remote-service-1/
http://www.woaitqs.cc/android/2016/05/30/android-binder-proxy-and-token
http://m.blog.csdn.net/article/details?id=51243539
http://blog.csdn.net/bjp000111/article/details/51919640
http://www.jianshu.com/p/39c1c2bf39c6
http://blog.csdn.net/luoyanglizi/article/details/51958091
http://www.cnblogs.com/whoislcj/p/5509868.html
http://www.jianshu.com/p/e642db926c50
http://www.jianshu.com/p/a5c73da2e9be
http://mp.weixin.qq.com/s?__biz=MjM5NzA0ODU0NA==&mid=2247483695&idx=1&sn=a2fca99eb3c5f1bd81cbf345fb2e2adb&scene=23&srcid=0706huZ15GukdCiE1hBGsLKR#rd
http://blog.csdn.net/luoyanglizi/article/details/51980630
http://blog.csdn.net/luoyanglizi/article/details/52029091
http://blog.csdn.net/ankas/article/details/52625273
http://www.jianshu.com/p/cfb1d2a109a2

http://blog.csdn.net/ethanchiu/article/details/19488423

https://github.com/manishkpr/Android-AIDL-Example
https://github.com/race604/AIDLService-sample
https://github.com/afollestad/aidl-example

你可能感兴趣的:(View体系6:ViewTree的遍历流程)