View的工作流程基本概念

ViewRootImpl 与 DecorView

ViewRootImpl是连接WindowManager和DecorView的纽带。

联系过程

Activity # makeVisible()

创建WindowManagerImpl对象并调用其addView()将DecorView传入。

void makeVisible() {
    if (!mWindowAdded) {
        // 返回WindowManagerImpl对象
        ViewManager wm = getWindowManager();
        // 传入DecorView,调用WindowManagerImpl的addView()
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
WindowManagerImpl # addView()

WindowManagerImpl的addView()其实调用了WindowManagerGlobal的addView()。

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    // 调用WindowManagerGlobal的addView()
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal # addView()

创建ViewRootImpl,并调用其setView()传入DecorView。

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    // ......
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
        // ......
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
        // 将相关加入WindowManagerGlobal的三个全局集合中
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
        try {
            // 将DecorView与ViewRootImpl连接起来
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}
ViewRootImpl # setView()

ViewRootImpl将DecorView赋值给全局变量mView,作为View链的根View。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        // 第一个setView的赋值给mView,表示根View
        if (mView == null) {
            mView = view;
            // ......
        }
    }
}

总结

WindowManagerGlobal中持有若干个ViewRootImpl,每个ViewRootImpl都会管理一条View链中所有View,一个Window可能存在多个View链,所以可能存在多个ViewRootImpl。比如以DecorView为根的View链,在DecorView中增删改子View都会用到ViewRootImpl。

拓展

Android视图框架Activity,Window,View,ViewRootImpl理解

MeasureSpec

MeasureSpec是View的一个静态内部类。在View的测量过程中需要用到一个32位int型的量,高2位表示SpecMode,低30位表示SpecSize。

SpecMode分类

  • UNSPECIFIED(00):父容器不对View做任何限制。
  • EXACTLY(01):精确大小,测量大小为SpecSize。对应具体数值或match_parent。
  • AT_MOST(10):父容器指定最大值。对应wrap_content。

MeasureSpec类源码

public static class MeasureSpec {
    // mode需要的偏移量
    private static final int MODE_SHIFT = 30;
    // 11 000000000000000000000000000000
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
    
    public @interface MeasureSpecMode {}
    // 三种SpecMode对应的值
    // 00 00...0
    public static final int UNSPECIFIED = 0 << MODE_SHIFT;
    // 01 00...0
    public static final int EXACTLY     = 1 << MODE_SHIFT;
    // 10 00...0
    public static final int AT_MOST     = 2 << MODE_SHIFT;
    
    // 合并SpecMode和SpecSize生成MeasureSpec
    public static int makeMeasureSpec(int size, int mode) {
        if (sUseBrokenMakeMeasureSpec) {
            return size + mode;
        } else {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }
    }
    // 加判断UNSPECIFIED
    public static int makeSafeMeasureSpec(int size, int mode) {
        if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
            return 0;
        }
        return makeMeasureSpec(size, mode);
    }
    // 从MeasureSpec中提取SpecMode
    @MeasureSpecMode
    public static int getMode(int measureSpec) {
        return (measureSpec & MODE_MASK);
    }
    // 从MeasureSpec中提取SpecMode
    public static int getSize(int measureSpec) {
        return (measureSpec & ~MODE_MASK);
    }
    // 调整MeasureSpec中的SpecSize
    static int adjust(int measureSpec, int delta) {
        final int mode = getMode(measureSpec);
        int size = getSize(measureSpec);
        // UNSPECIFIED不需要调整
        if (mode == UNSPECIFIED) {
            return makeMeasureSpec(size, UNSPECIFIED);
        }
        // 调整SpecSize
        size += delta;
        if (size < 0) {
            Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
                    ") spec: " + toString(measureSpec) + " delta: " + delta);
            size = 0;
        }
        // 重新打包
        return makeMeasureSpec(size, mode);
    }
    
    public static String toString(int measureSpec) {
        int mode = getMode(measureSpec);
        int size = getSize(measureSpec);
        StringBuilder sb = new StringBuilder("MeasureSpec: ");
        if (mode == UNSPECIFIED)
            sb.append("UNSPECIFIED ");
        else if (mode == EXACTLY)
            sb.append("EXACTLY ");
        else if (mode == AT_MOST)
            sb.append("AT_MOST ");
        else
            sb.append(mode).append(" ");
        sb.append(size);
        return sb.toString();
    }
}

MeasureSpec的创建依据

  • 对于DecorView的确定因素
    • 窗口尺寸
    • 子身的LayoutParams
  • 对于普通View的确定因素
    • 父布局的MeasureSpec
    • 自身的LayoutParams

一个View的MeasureSpec确定后,View的宽高会在onMeasure()根据MeasureSpec来确定。

DecorView的MeasureSpec创建

View的绘制流程是从ViewRootImpl的performTraversals()开始的,其中会调用measureHierarchy()创建DecorView的MeasureSpec。

ViewRootImpl # measureHierarchy()
// ......
// desiredWindowWidth为窗口的宽度,lp.width为DecorView的layourParams中设置的宽度
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
// 得到DecorView宽高的MeasureSpec后调用performMeasure()去完成DecorView的测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// ......
ViewRootImpl # getRootMeasureSpec()

根据window尺寸和DecorView的LayourParams来决定DecorView的MeasureSpec。

// windowSize就是Window的大小,rootDimension指的是DecorView的layoutParams中设置的大小
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {
    // match_parent和fill_parent的值都是-1
    case ViewGroup.LayoutParams.MATCH_PARENT: windowSize.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    case ViewGroup.LayoutParams.WRAP_CONTENT:
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    // DecorView设置的具体数值
    default:
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
}
ViewRootImpl # performMeasure()

传入创建好的MeasureSpec,调用View的measure()开始测量过程。

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        // 开始测量过程
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}
总结

DecorView的MeasureSpec由自身LayoutParams和Window尺寸决定。

layoutParams\MeasureSpec SpecSize SpecMode
MATCH_PARENT windowSize EXACTLY
WRAP_CONTENT windowSize AT_MOST
具体数值 rootDimension EXACTLY

普通View的MeasureSpec创建

普通View的measure()是由ViewGroup的measureChildWithMargins()调用的。

ViewGroup # measureChildWithMargins()
protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {
    // 参数中的两个MeasureSpec是ViewGroup的MeasureSpec
    // widthUsed和heightUsed已经占用的宽度和高度
    final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    // 传入ViewGroup的MeasureSpec、ViewGroup中不属于自己的大小、View的layoutParams中定义的大小。
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                    + widthUsed, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                    + heightUsed, lp.height);
    // 计算出MeasureSpec后开始进行测量流程。
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
ViewGroup # getChildMeasureSpec()
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    // 父布局的Mode和Size
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    // 父布局原大小减去已用大小如果小于0,就取0
    int size = Math.max(0, specSize - padding);
    int resultSize = 0;
    int resultMode = 0;
    switch (specMode) {
    // 如果父布局Mode是EXACTLY
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            // 自身设置的是具体数值
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // 自身设置的是match_parent
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // 自身设置的是wrap_content
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // 父布局Mode为AT_MOST
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // 自身设置的是具体数值
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // 自身设置的是match_parent
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // 自身设置的是wrap_content
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {
            // 自身设置的是具体数值
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // 自身设置的是match_parent
            // 如果设置了sUseZeroUnspecifiedMeasureSpec,大小就是0
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // 自身设置的是wrap_content
            // 如果设置了sUseZeroUnspecifiedMeasureSpec,大小就是0
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    // 合并返回
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
总结

普通View的MeasureSpec由父布局的MeasureSpec和自身的LayoutParams决定。

自身lp\父布局mode EXACTLY AT_MOST UNSPECIFIED
具体数值 childSize EXACTLY childSize EXACTLY childSize EXACTLY
match_parent parentSize EXACTLY parentSize AT_MOST 0 or parentSize UNSPECIFIED
wrap_content parentSize AT_MOST parentSize AT_MOST 0 or parentSize UNSPECIFIED

你可能感兴趣的:(View的工作流程基本概念)