AndroidUI-View绘制流程

65 阅读4分钟

源码:Android11

执行主线注释标识:// ####

省略代码标识 // ...

PS:不定期更新内容

场景1:Activity绘制流程

// activity中执行resume方法的源码位置
ActivityThread.java
    public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
        // #### 执行activity的onResume方法 1->A
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
            // #### 这里获取PhoneWindow
            r.window = r.activity.getWindow();
            // #### 这里获取DecorView
            View decor = r.window.getDecorView();
                    // #### 这里wm是WindowManagerGlobal  1->B
                    wm.addView(decor, l);
    }
    public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
            // #### 执行activity的onResume方法 1->A
            r.activity.performResume(r.startsNotResumed, reason);
    }
Activity.java
    final void performResume(boolean followedByPause, String reason) {
        // #### 执行activity的onResume方法 结束1->A
        mInstrumentation.callActivityOnResume(this);
    }
WindowManagerGlobal.java
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
            // #### 构建ViewRootImpl 1->B->A
            root = new ViewRootImpl(view.getContext(), display);
            // #### 添加DecorView  1->B
            mViews.add(view);
            // #### 添加ViewRootImpl  1->B
            mRoots.add(root);
            // #### 添加LayoutParams  1->B
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                // #### 最后把View添加到ViewRootImpl  1->B
                root.setView(view, wparams, panelParentView, userId);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
    }
ViewRootImpl.java
    public ViewRootImpl(Context context, Display display, IWindowSession session,
            boolean useSfChoreographer) {
        // #### 记录当前线程作为后面判断是否在UI线程刷新的依据 结束1->B->A
        mThread = Thread.currentThread();
        // #### 记录当前窗口的数据 1->B->A
        mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
                context);
    }
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
                // #### 请求遍历 1->B
                requestLayout();
                    // #### 将窗口PhoneWindow添加到WMS 1->B
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
    }
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            // #### 这里会检查当前刷新UI的线程是否与初始化ViewRootImpl的线程一致,也就是分析1->B->A流程的关联上
            checkThread();
            mLayoutRequested = true;
            // #### 请求刷新UI 1->B
            scheduleTraversals();
        }
    }
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // #### 向Choreographer发送刷新UI的请求,最终执行mTraversalRunnable 1->B
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
             // #### 刷新UI 1->B
            doTraversal();
        }
    }
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            // #### 刷新UI 1->B
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
    private void performTraversals() {
                    // #### 刷新UI 结束1->B
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

场景2:View测量流程

ViewRootImpl.java
// 根据上面的绘制流程
    private void performTraversals() {
                    // #### 这里会先进行最多三次的预测量 2->A
            windowSizeMayChange |= measureHierarchy(host, lp, res,
                    desiredWindowWidth, desiredWindowHeight);
                    // #### 进行遍历测量 2->B
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
            final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
        int childHeightMeasureSpec;
        boolean windowSizeMayChange = false;

        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(mTag,
                "Measuring " + host + " in display " + desiredWindowWidth
                + "x" + desiredWindowHeight + "...");

        boolean goodMeasure = false;
        if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            // On large screens, we don't want to allow dialogs to just
            // stretch to fill the entire width of the screen to display
            // one line of text.  First try doing the layout at a smaller
            // size to see if it will fit.
            final DisplayMetrics packageMetrics = res.getDisplayMetrics();
            res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
            int baseSize = 0;
            if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
                baseSize = (int)mTmpValue.getDimension(packageMetrics);
            }
            if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": baseSize=" + baseSize
                    + ", desiredWindowWidth=" + desiredWindowWidth);
            if (baseSize != 0 && desiredWindowWidth > baseSize) {
                childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                // #### 第一次进行系统默认尺寸的预测量是否满足 2->A
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                        + host.getMeasuredWidth() + "," + host.getMeasuredHeight()
                        + ") from width spec: " + MeasureSpec.toString(childWidthMeasureSpec)
                        + " and height spec: " + MeasureSpec.toString(childHeightMeasureSpec));
                if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                    goodMeasure = true;
                } else {
                    // Didn't fit in that size... try expanding a bit.
                    baseSize = (baseSize+desiredWindowWidth)/2;
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
                            + baseSize);
                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                    // #### 第二次进行大一倍尺寸的预测量是否满足 2->A
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": measured ("
                            + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
                    if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                        if (DEBUG_DIALOG) Log.v(mTag, "Good!");
                        goodMeasure = true;
                    }
                }
            }
        }

        if (!goodMeasure) {
            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
            // #### 第三次进行最大尺寸的预测量是否满足 2->A
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
                windowSizeMayChange = true;
            }
        }

        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals -- after measure");
            host.debug();
        }
        // #### 返回三次预测量是否满足的判断 接受2->A
        return windowSizeMayChange;
    }
    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        if (mView == null) {
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            // #### 执行View的onMeasure 2->B
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
View.java
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
                // #### 执行View的onMeasure 结束2->B
                onMeasure(widthMeasureSpec, heightMeasureSpec);
            // #### 根据以下抛出的异常知道,自定义view或者viewgroup重写onMeasure方法必须调用setMeasuredDimension方法 2->B
            if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
                throw new IllegalStateException("View with id " + getId() + ": "
                        + getClass().getName() + "#onMeasure() did not set the"
                        + " measured dimension by calling"
                        + " setMeasuredDimension()");
            }
            }
    }

场景3:子线程更新UI

// 根据场景2分析知道,如果要在子线程更新UI的主要问题是绕过1->B->A流程的mThread的判断,所以可以从两方面入手,一个是创建ViewRootImpl前触发刷新UI,一个是子线程自己创建ViewRootImpl
// 从场景1、2我们知道初始化Activity如何渲染UI,现在再看下View自己如何触发刷新UI的流程
View.java
    public void invalidate(boolean invalidateCache) {
        // #### 3->A
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }
    void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
                // #### 3->A
                p.invalidateChild(this, damage);
    }
ViewGroup.java
    public final void invalidateChild(View child, final Rect dirty) {
                // #### 3->A
                parent = parent.invalidateChildInParent(location, dirty);
    }
ViewRootImpl.java
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
            // #### 这里又出现了判断刷新UI是否一直的方法 3->A->A
            checkThread();
            // #### 结束3->A
            invalidate();
    }
    void checkThread() {
        // #### 所以要再子线程刷新UI的方法就是绕过这个mThread的初始化或者子线程就是mThread 结束3->A->A
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }