Android View 的绘制流程


view作为构成android界面的基本元素,深入了解view的绘制流程对开发人员来说是很有必要的。

我们创建一个Activity都会在onCreate方法中写setContentView(layoutResId),把我们的布局传进去,那此时我们的布局就添加到屏幕上了吗?当然不是的。那我们的这个布局是在什么时候添加进屏幕的呢?

在ActivityThread.java 的handleResumeActivity中

java 复制代码
//ActivityThread.java
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,boolean
isForward, String reason) {
//...
wm.addView(decor, l);
//...
}

这里调用wm的addView,才把DecorView添加到窗口,wm其实就是WindowManagerImpl,接着我们跟进到WindowManagerImpl的addView

java 复制代码
//WindowManagerImpl.java
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyTokens(params);
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
    mContext.getUserId());
}

这里调用的是mGlobal的addView方法,mGlobal其实就是WindowManagerGlobal,是管理整个进程所有窗口信息的,接下来看看WindowManagerGlobal的addView方法

java 复制代码
public void addView(View view, ViewGroup.LayoutParams params,
    Display display, Window parentWindow, int userId) {
    //...
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    ViewRootImpl root;
    if (windowlessSession == null) {
        root = new ViewRootImpl(view.getContext(), display);
     } else {
         root = new ViewRootImpl(view.getContext(), display,windowlessSession);
     }
//为DecorView设置LayoutParams
    view.setLayoutParams(wparams);
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);

    root.setView(view, wparams, panelParentView, userId);
    //...  
}

接下来才到关键的地方了,ViewRootImpl的setView方法

java 复制代码
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
    //...
    //遍历view树逻辑
    requestLayout();
    //...
    //将窗口添加到WMS上面
    res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
            getHostVisibility(),mDisplay.getDisplayId(), userId,
                            mInsetsController.getRequestedVisibilities(), inputChannel,
                            mTempInsets,mTempControls);
    //...
    //将ViewRootImpl设置为DecorView的parent
    view.assignParent(this);
}

在这里的requestLayout,最终会走到performTraversals方法,这里才真正开始遍历view的measure、layout、draw等流程

java 复制代码
private void performTraversals() {
    //...
    //预测量,最多会执行3次onMeasure
    // Ask host how big it wants to be
    windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
    desiredWindowWidth, desiredWindowHeight);
    //...
    // Ask host how big it wants to be
    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    //...
    performLayout(lp, mWidth, mHeight);
    //...
    if (!performDraw() && mSyncBufferCallback != null) {
        mSyncBufferCallback.onBufferReady(null);
    }
    //...
}

预测量

java 复制代码
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
    final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
    //...
  
    if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
        final DisplayMetrics packageMetrics = res.getDisplayMetrics();
        //默认320dp
        res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
        if (baseSize != 0 && desiredWindowWidth > baseSize) {
            childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width,lp.privateFlags);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
                        lp.privateFlags);
            //第一次预测量
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            if((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0 {
                goodMeasure = true;
            } else {
                //宽度不够则再将剩余空间一般给child继续测量
                baseSize = (baseSize + desiredWindowWidth) / 2;
                childWidthMeasureSpec = getRootMeasureSpec(baseSize,
                    lp.width,lp.privateFlags);
                performMeasure(childWdithMeasureSpec,childHeightMeasureSpec);
                if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                        if (DEBUG_DIALOG) Log.v(mTag, "Good!");
                        goodMeasure = true;
                 }
            }
        }
    }
           //如果宽度还不满足,则将全部宽度给child去测量
        if (!goodMeasure) {
            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width,
                    lp.privateFlags);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height,
                    lp.privateFlags);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
            {
                windowSizeMayChange = true;
            }
        }
}

测量

java 复制代码
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);
        }
}
java 复制代码
//View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    //...
    //最终执行到对应view的onMeasure中
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    //...
}

布局

java 复制代码
//ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {

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


//View.java
public void layout(int l, int t, int r, int b) {
    //...
    //在这里给左上右下去赋值,之后的getWidth和getHeight才能获取到值
    boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
    //...
    //这里调用到对应view的onLayout方法
    onLayout(changed, l, t, r, b);
    //...
}

绘制

java 复制代码
//ViewRootImpl.java
private boolean performDraw() {
    //...
    boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
    //...
}

private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
    //...
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
        scalingRequired, dirty, surfaceInsets)) {
        return false;
    }
}

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
    //...
    //执行到对应view的draw方法
    mView.draw(canvas);
    //...
}
相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui