美文网首页
View绘制流程分析-基于Android API31

View绘制流程分析-基于Android API31

作者: Ray206 | 来源:发表于2022-05-07 17:26 被阅读0次

    View从setContentViewonMeasure->onLayout->onDraw经历哪些流程。下面就来梳理哈View的绘制流程

    setContentView做了什么工作?

    时序图
    Activity.setContentView
    public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
    }
    

    这里调用PhoneWindowsetContentView,然后在初始化ActionBar

    PhoneWindow.setContentView
        public void setContentView(int layoutResID) {
            ...
              //第一次调用这里必为空
              if (mContentParent == null) {
                //创建DecorView,并将ContentParent区域的View对象赋值给mContentParent
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
             mLayoutInflater.inflate(layoutResID, mContentParent);
            ...
        }
    

    创建DecorView,在创建DecorView时也将Window设置DecorView

    LayoutInflater.inflate
        public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
            return inflate(resource, root, root != null);
        }
    
        public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
            final Resources res = getContext().getResources();
            if (DEBUG) {
                Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
                      + Integer.toHexString(resource) + ")");
            }
            //预加载的View,比会返回null
            View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
            if (view != null) {
                return view;
            }
            //得到资源解析器
            XmlResourceParser parser = res.getLayout(resource);
            try {
                return inflate(parser, root, attachToRoot);
            } finally {
                parser.close();
            }
        }
    
    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
            synchronized (mConstructorArgs) {
                ...
             
                    final String name = parser.getName();
    
                    if (DEBUG) {
                        System.out.println("**************************");
                        System.out.println("Creating root view: "
                                + name);
                        System.out.println("**************************");
                    }
                    //处理merge标签
                    if (TAG_MERGE.equals(name)) {
                        if (root == null || !attachToRoot) {
                            throw new InflateException("<merge /> can be used only with a valid "
                                    + "ViewGroup root and attachToRoot=true");
                        }
    
                        rInflate(parser, root, inflaterContext, attrs, false);
                    } else {
                        // 创建本层级View,也就是xml中最外层的ViewGroup,并加入到ContentParent中
                        final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    
                        ViewGroup.LayoutParams params = null;
    
                        if (root != null) {
                            if (DEBUG) {
                                System.out.println("Creating params from root: " +
                                        root);
                            }
                            // Create layout params that match root, if supplied
                            params = root.generateLayoutParams(attrs);
                            if (!attachToRoot) {
                                // Set the layout params for temp if we are not
                                // attaching. (If we are, we use addView, below)
                                temp.setLayoutParams(params);
                            }
                        }
    
                        if (DEBUG) {
                            System.out.println("-----> start inflating children");
                        }
    
                        // 递归创建之View,并添加到他的父View中
                        rInflateChildren(parser, temp, attrs, true);
                    }
                        ...
                return result;
            }
        }
    

    createViewFromTag:方法创建xml中最外层父ViewGroup,将该View添加至ContentParent中,并设置ViewGroupLayoutParams.
    rInflateChildren:使用递归的方法去创建子View,并添加到他们的父View

    这样setContentView的工作就结束了,setContentView的工作加载xml解析后生成View,填充到DecorViewContentParent中。xml资源转为对象,并填充至DecorView
    下面我们继续看View的绘制

    View绘制

    在上一篇《Activity启动流程-基于Android API31》中有介绍到View在ActivityThread.handleResumeActivity中进行绘制

    时序图
    ActivityThread.handleResumeActivity
        public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
                boolean isForward, String reason) {
            ...
            //执行onResume方法
            if (!performResumeActivity(r, finalStateRequest, reason)) {
                return;
            }
            ...
            //添加DecorView
            if (r.window == null && !a.mFinished && willBeVisible) {
                ...
                if (a.mVisibleFromClient) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                         //添加decor,并使用Handler发送异步消息,用于View绘制
                        wm.addView(decor, l);
                    } else {
                        // The activity will get a callback for this {@link LayoutParams} change
                        // earlier. However, at that time the decor will not be set (this is set
                        // in this method), so no action will be taken. This call ensures the
                        // callback occurs with the decor set.
                        a.onWindowAttributesChanged(l);
                    }
                }
           ...
        }
    
    WindowManagerImpl.addView

    WindowManager的实现类

        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            //赋值windowToken, PopupDialog多次添加同一个PopupDialog时提示token问题就是这个
            applyTokens(params);
            mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                    mContext.getUserId());
        }
    
    WindowManagerGlobal.addView

    是一个单例,管理ViewRootImplRootView

        public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow, int userId) {
             ...
            ViewRootImpl root;
            View panelParentView = null;
    
            synchronized (mLock) {
                ...
                创建ViewRootImpl实例
                root = new ViewRootImpl(view.getContext(), display);
                //设置LayoutParams
                view.setLayoutParams(wparams);
                //WindowManagerGlobal 记录信息
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
    
                // do this last because it fires off messages to start doing things
                try {
                    //DecorView设置给ViewRootImpl
                    root.setView(view, wparams, panelParentView, userId);
                } catch (RuntimeException e) {
                    // BadTokenException or InvalidDisplayException, clean up.
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                    throw e;
                }
            }
        }
    
    ViewRootImpl.setView

    View的顶层类。WindowManagerGlobal为Window提供View管理,WindowManagerGlobal管理所有的ViewRootViewRootImplViewRootImpl更像是WindowManagerGlobal的具体实现类。

        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
                int userId) {
            ....
            requestLayout();
            ...
           //IPC 通信,告诉WindowManagerService 要创建Window
          //这里的IWindowSession实现类为“com.android.server.wm.Session”,最终方法内调用WindowManagerService.addWindow添加window
          //res,返回添加状态详细参考WindowManagerGlobal.ADD_xxxxx
            res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), userId,
                    mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
                    mTempControls);
           ...
        }
    
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                //检测主线程,不在就抛异常
                checkThread();
                mLayoutRequested = true;
                //开启调度
                scheduleTraversals();
            }
        }
    
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                //发送屏障消息,为绘制做准备
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                //添加绘制回调
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    
    
    1. 第一步发送消息屏障
    2. 第二步添加VSYNC信号监听(以60帧率计算 1s/60 最大16.6ms刷新一次),当VSYNC信号来时回调mTraversalRunnable

    消息同步屏障postSyncBarrier()发送同步屏障消息,这个消息会使Handler停止处理同步普通消息,只处理异步消息。发送屏障消息时,如果Handler处于阻塞状态也不会去唤醒。
    异步消息:普通message调用setAsynchronous(true),即可成为异步消息,当设置了同步屏障后,优先同步消息执行
    如果未发送屏障消息,异步消息和同步消息一样,不会获得优先执行的权利。
    这里设置同步屏障,因为马上要进行界面绘制,避免其他耗时消息导致绘制延迟。

    Choreographer.postCallback

    这个类对上层应用提供VSYNC信号监听(FrameDisplayEventReceiver),当有信号量时处理调监听回调。对底层起到接收VSYNC信号的作用(FrameDisplayEventReceiver

        public void postCallback(int callbackType, Runnable action, Object token) {
            postCallbackDelayed(callbackType, action, token, 0);
        }
    
        public void postCallbackDelayed(int callbackType,
                Runnable action, Object token, long delayMillis) {
            if (action == null) {
                throw new IllegalArgumentException("action must not be null");
            }
            if (callbackType < 0 || callbackType > CALLBACK_LAST) {
                throw new IllegalArgumentException("callbackType is invalid");
            }
    
            postCallbackDelayedInternal(callbackType, action, token, delayMillis);
        }
    
        private void postCallbackDelayedInternal(int callbackType,
                Object action, Object token, long delayMillis) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "PostCallback: type=" + callbackType
                        + ", action=" + action + ", token=" + token
                        + ", delayMillis=" + delayMillis);
            }
    
            synchronized (mLock) {
                final long now = SystemClock.uptimeMillis();
                final long dueTime = now + delayMillis;
                //添加回调
                mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
                //延迟时间是否到了,一般情况都是true
                if (dueTime <= now) {
                    scheduleFrameLocked(now);
                } else {
                    //还未到时间发送一个异步消息
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                    msg.arg1 = callbackType;
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, dueTime);
                }
            }
        }
    
        private void scheduleFrameLocked(long now) {
            if (!mFrameScheduled) {
                mFrameScheduled = true;
                if (USE_VSYNC) {//true
                    if (DEBUG_FRAMES) {
                        Log.d(TAG, "Scheduling next frame on vsync.");
                    }
                    //当前线程是否和looper线程一致
                    if (isRunningOnLooperThreadLocked()) {
                        //请求信号量
                        scheduleVsyncLocked();
                    } else {
                        //不一致用Handler切换到Looper线程
                        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageAtFrontOfQueue(msg);
                    }
                } else {
                    //老版本使用Handler去处理绘制,新版本使用VSYNC信号
                    final long nextFrameTime = Math.max(
                            mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                    if (DEBUG_FRAMES) {
                        Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                    }
                    Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextFrameTime);
                }
            }
        }
    
        private void scheduleVsyncLocked() {
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#scheduleVsyncLocked");
                //请求信号量,实际调用 DisplayEventReceiver.nativeScheduleVsync(mReceiverPtr);
                mDisplayEventReceiver.scheduleVsync();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    

    Choreographer.FrameDisplayEventReceiverChoreographer的内部类继承DisplayEventReceiver用于监听信号量回调,当注册后,有了信号量会回调FrameDisplayEventReceiver.dispatchVsync方法,该方法为重写所以会调用父类DisplayEventReceiver.dispatchVsync
    这里有用到2个同步消息的地方,一个为了切换线程,一个是老版本的利用Handler异步消息去绘制界面

    FrameDisplayEventReceiver.dispatchVsync
         private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable {
             ...
            // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
            // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
            // for the internal display implicitly.
            @Override
            public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
                    VsyncEventData vsyncEventData) {
                try {
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                        Trace.traceBegin(Trace.TRACE_TAG_VIEW,
                                "Choreographer#onVsync " + vsyncEventData.id);
                    }
                    // Post the vsync event to the Handler.
                    // The idea is to prevent incoming vsync events from completely starving
                    // the message queue.  If there are no messages in the queue with timestamps
                    // earlier than the frame time, then the vsync event will be processed immediately.
                    // Otherwise, messages that predate the vsync event will be handled first.
                    long now = System.nanoTime();
                    if (timestampNanos > now) {
                        Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                                + " ms in the future!  Check that graphics HAL is generating vsync "
                                + "timestamps using the correct timebase.");
                        timestampNanos = now;
                    }
    
                    if (mHavePendingVsync) {
                        Log.w(TAG, "Already have a pending vsync event.  There should only be "
                                + "one at a time.");
                    } else {
                        mHavePendingVsync = true;
                    }
    
                    mTimestampNanos = timestampNanos;
                    mFrame = frame;
                    mLastVsyncEventData = vsyncEventData;
                    //发送同步消息,Callback为this。
                    Message msg = Message.obtain(mHandler, this);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
            }
    
            @Override
            public void run() {
                //消息回调执行这里
                mHavePendingVsync = false;
                doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
            }
        }
    

    这里为什么使用Handler去异步消息后执行run方法,不直接调用run方法呢?应该也是为了切换线程

    Choreographer.doFrame
      void doFrame(long frameTimeNanos, int frame,
                DisplayEventReceiver.VsyncEventData vsyncEventData) {
                     ...
                    //掉帧日志
                    final long jitterNanos = startNanos - frameTimeNanos;
                    if (jitterNanos >= frameIntervalNanos) {
                        final long skippedFrames = jitterNanos / frameIntervalNanos;
                        if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                            Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                                    + "The application may be doing too much work on its main thread.");
                        }
                        final long lastFrameOffset = jitterNanos % frameIntervalNanos;
                        if (DEBUG_JANK) {
                            Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                                    + "which is more than the frame interval of "
                                    + (frameIntervalNanos * 0.000001f) + " ms!  "
                                    + "Skipping " + skippedFrames + " frames and setting frame "
                                    + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                        }
                        frameTimeNanos = startNanos - lastFrameOffset;
                    }
    
                    if (frameTimeNanos < mLastFrameTimeNanos) {
                        if (DEBUG_JANK) {
                            Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                                    + "previously skipped frame.  Waiting for next vsync.");
                        }
                        traceMessage("Frame time goes backward");
                        scheduleVsyncLocked();
                        return;
                    }
    
                    if (mFPSDivisor > 1) {
                        long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                        if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                            traceMessage("Frame skipped due to FPSDivisor");
                            scheduleVsyncLocked();
                            return;
                        }
                    }
    
                    mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
                            vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
                    mFrameScheduled = false;
                    mLastFrameTimeNanos = frameTimeNanos;
                    mLastFrameIntervalNanos = frameIntervalNanos;
                    mLastVsyncEventData = vsyncEventData;
                }
    
                AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
                mFrameInfo.markInputHandlingStart();
                //输入事件回调处理
                doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
    
                mFrameInfo.markAnimationsStart();
                //动画回调处理
                doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
                doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos,
                        frameIntervalNanos);
    
                mFrameInfo.markPerformTraversalsStart();
                //消息绘制处理
                doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
                //Commit 相关回调
                doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
            } finally {
                AnimationUtils.unlockAnimationClock();
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
    
            if (DEBUG_FRAMES) {
                final long endNanos = System.nanoTime();
                Log.d(TAG, "Frame " + frame + ": Finished, took "
                        + (endNanos - startNanos) * 0.000001f + " ms, latency "
                        + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
            }
        }
    
    void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
            CallbackRecord callbacks;
            synchronized (mLock) {
                final long now = System.nanoTime();
                //根据回调类型,取出所有回调
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                        now / TimeUtils.NANOS_PER_MS);
                if (callbacks == null) {
                    return;
                }
                mCallbacksRunning = true;
            ...
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
                //遍历回调
                for (CallbackRecord c = callbacks; c != null; c = c.next) {
                    if (DEBUG_FRAMES) {
                        Log.d(TAG, "RunCallback: type=" + callbackType
                                + ", action=" + c.action + ", token=" + c.token
                                + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                    }
                    //执行run方法
                    c.run(frameTimeNanos);
                }
            } finally {
                synchronized (mLock) {
                    //回收掉所有的回调对象
                    mCallbacksRunning = false;
                    do {
                        final CallbackRecord next = callbacks.next;
                        recycleCallbackLocked(callbacks);
                        callbacks = next;
                    } while (callbacks != null);
                }
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    
    1. 取出回调并执行run方法
    2. 回收回调对象。因为绘制(包括绘制在内的,动画,输入事件等回调事件超级多)非常超级频繁,如果一直创建和回收会造成内存抖动。

    这里的run方法为Choreographer.postCallback方法传入的TraversalRunnable对象

    TraversalRunnable.run
        final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }
    
    ViewRootImpl.performTraversals
        void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                //删除屏障信息
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
    
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
                //绘制
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }
    

    删除屏障信息,让Handler获得继续处理同步消息的能力

    ViewRootImpl.performTraversals
        private void performTraversals() {
          ...
            //window的宽高
            int desiredWindowWidth;
            int desiredWindowHeight;
            ...
                //根据window宽高去测试View树,是否需要修改Window,这个方法中最少执行一次onMeasure,至多执行三次
                windowSizeMayChange |= measureHierarchy(host, lp, res,
                        desiredWindowWidth, desiredWindowHeight);
            ...
                //重新设置Window,并且关联surface
                relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
            ...
                //再次测量
                 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
           ...
            if (didLayout) {
                //调用Layout
                performLayout(lp, mWidth, mHeight);
            }
            boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
            if (!cancelDraw) {
                //调用Draw
                performDraw();
            }
        }
    
    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;
            //宽度为WRAP_CONTENT
            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);
                //window的宽度是否大于基础尺寸
                if (baseSize != 0 && desiredWindowWidth > baseSize) {
                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                    childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
                    //第一次测量
                    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 {
                        //子view想要更大的空间
                        baseSize = (baseSize+desiredWindowWidth)/2;
                        if (DEBUG_DIALOG) Log.v(mTag, "Window " + mView + ": next baseSize="
                                + baseSize);
                        childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                        //尺寸不合适,再次测量
                        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);
                //第三次测量
                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();
            }
    
            return windowSizeMayChange;
        }
    
        private static int getRootMeasureSpec(int windowSize, int rootDimension) {
            int measureSpec;
            switch (rootDimension) {
    
            case ViewGroup.LayoutParams.MATCH_PARENT:
                //RootView 希望填充Window,则满足它,此时它尺寸是确切值
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
                break;
            case ViewGroup.LayoutParams.WRAP_CONTENT:
                //RootView 希望根据自身内容来确定尺寸,则设置为AT_MOST 模式
                measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
                break;
            default:
               //RootView 希望直接指定尺寸值,则满足它,此时它尺寸是确切值
                measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
                break;
            }
            return measureSpec;
        }
    
      private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
                boolean insetsPending) throws RemoteException {
            ...
            //根据测量的view树的值,重新设置window大小
            int relayoutResult = mWindowSession.relayout(mWindow, params,
                    (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                    (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                    insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                    mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                    mTempControls, mSurfaceSize);
            mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
            if (mSurfaceControl.isValid()) {
                if (!useBLAST()) {
                    //关联Window和surface
                    mSurface.copyFrom(mSurfaceControl);
                } else {
                    final Surface blastSurface = getOrCreateBLASTSurface();
                    // If blastSurface == null that means it hasn't changed since the last time we
                    // called. In this situation, avoid calling transferFrom as we would then
                    // inc the generation ID and cause EGL resources to be recreated.
                    if (blastSurface != null) {
                        mSurface.transferFrom(blastSurface);
                    }
                }
            ...
            ///RootView记录window大小
            setFrame(mTmpFrames.frame);
            ...
            return relayoutResult;
        }
    

    首先获取到window的宽高,然后使用measureHierarchy去测试ViewTree,看是否需要重新设置window大小。

    measureHierarchy三次测量

    1. 先用内置的基础值测量ViewTree
    2. ViewTree要得更大。于是加大基础值,再次测量
    3. 第二次测量还是不满足ViewTree,这次使用window的最大宽度再次测量

    设置Window的宽高为当天ViewTree的宽高,并关联surface
    重新设置好Window后再次测量,然后调用layoutdraw绘制。
    总结
    setContextVIew:将xml资源转化成View对象
    onMeasure:测量View大小
    onLayout:确定View位置
    onDraw:绘制View对象
    实际上onDraw后我们仍然是看不到图形的,因为onDraw方法只是将View对象转成一个buffer对象,需要SurfaceFlinger合成,发送至屏幕,才可以见到真正的图片。
    View对象是如何转成buffer的呢,这里就会用到上面绑定Surface

    1. Surface通过dequeueBuffer获取到一块绘制缓冲区
    2. Canvasdraw时会调用底层Skia引擎进行数据写入
    3. Surface通过queueBuffer提交数据到SurfaceFlinger
    4. SurfaceFlinger接受区对这些数据进行合,发送到屏幕显示

    SurfaceFlinger:接受缓冲区,对它们进行合并,然后发送到屏幕显示

    !!over!!

    相关文章

      网友评论

          本文标题:View绘制流程分析-基于Android API31

          本文链接:https://www.haomeiwen.com/subject/pmnryrtx.html