美文网首页android技术
Android渲染绘制流程

Android渲染绘制流程

作者: seagazer | 来源:发表于2020-07-15 15:20 被阅读0次
    Android应用层是不涉及SurfaceFlinger,FrameBuffer之类的底层框架,常用刷新视图都是在Viewdraw相关方法中进行标准绘制api操作,然后通过View.invalidate或者View.requestLayout通知系统进行视图显示的刷新。在此不讨论draw相关的apidraw的所有绘制方法都是直接jni调用对应skia的绘制,具体的自己查看skia引擎相关的资料。
    其实View.invalidate或者View.requestLayout最终走向的流程基本一致,都是一层层向上遍历,最终进入视图管理器ViewRootImpl中进行下一次的vsync信号请求,在接收到信号后进行视图渲染的刷新。
    1.简单分析下View.invalidate:
    1.1 比如自定义View动画,通常都是根据属性动画进度计算出此时刻需要展示的效果,然后进行invalidate或者postInvalidate进行刷新。
            anim.addUpdateListener {
                val percent: Float = it.animatedValue as Float
                curDistance = maxDistance * percent
                invalidate()// 重绘
            }
    
            override fun onDraw(canvas: Canvas?) {
                canvas!!.drawLine(0F, paint.strokeWidth, curDistance, paint.strokeWidth, paint)
            }        
    
    1.2 那就从invalidate开始分析:
    <View.java>
        // invalidate最终调用的是invalidateInternal
        void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
                boolean fullInvalidate) {
            if (mGhostView != null) {// 幽灵视图,可以理解为一个状态不可见view的拷贝,类似overlay图层概念
                mGhostView.invalidate(true);
                return;
            }
    
            if (skipInvalidate()) {// 不需要重绘,return
                return;
            }
    
            // Reset content capture caches
            mCachedContentCaptureSession = null;
    
            if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                    || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                    || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
                    || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
                if (fullInvalidate) {// 全局刷新
                    mLastIsOpaque = isOpaque();
                    mPrivateFlags &= ~PFLAG_DRAWN;
                }
    
                mPrivateFlags |= PFLAG_DIRTY;
    
                if (invalidateCache) {// 视图缓存相关标记
                    mPrivateFlags |= PFLAG_INVALIDATED;
                    mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
                }
    
                // Propagate the damage rectangle to the parent view.
                final AttachInfo ai = mAttachInfo;// attachInfo之前分析过,整个视图树共享
                final ViewParent p = mParent;// 父View
                if (p != null && ai != null && l < r && t < b) {
                    final Rect damage = ai.mTmpInvalRect;
                    damage.set(l, t, r, b);
                    p.invalidateChild(this, damage);// 通知父ViewGroup重绘自己
                }
                // 下面的damageInParent是新的api,暂时还是分析上面invalidateChild方法的重绘流程,流程最终走向都是一致的
    
                // Damage the entire projection receiver, if necessary.
                if (mBackground != null && mBackground.isProjected()) {
                    final View receiver = getProjectionReceiver();
                    if (receiver != null) {
                        receiver.damageInParent();
                    }
                }
            }
        }
    
    • invalidate最终调用invalidateInternal,内部会通知父View对自己进行重绘,看一下ViewGroup.invalidateChild方法
    <ViewGroup.java>
        @Override
        public final void invalidateChild(View child, final Rect dirty) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null && attachInfo.mHardwareAccelerated) {
                // HW accelerated fast path
                onDescendantInvalidated(child, child);// 硬件加速绘制
                return;
            }
    
            ViewParent parent = this;
            if (attachInfo != null) {
                // If the child is drawing an animation, we want to copy this flag onto
                // ourselves and the parent to make sure the invalidate request goes
                // through
                final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
    
                // Check whether the child that requests the invalidate is fully opaque
                // Views being animated or transformed are not considered opaque because we may
                // be invalidating their old position and need the parent to paint behind them.
                Matrix childMatrix = child.getMatrix();
                // Mark the child as dirty, using the appropriate flag
                // Make sure we do not set both flags at the same time
    
                if (child.mLayerType != LAYER_TYPE_NONE) {
                    mPrivateFlags |= PFLAG_INVALIDATED;
                    mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
                }
                // 计算需要重绘view的绘制区域
                final int[] location = attachInfo.mInvalidateChildLocation;
                location[CHILD_LEFT_INDEX] = child.mLeft;
                location[CHILD_TOP_INDEX] = child.mTop;
                if (!childMatrix.isIdentity() ||
                        (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
                    RectF boundingRect = attachInfo.mTmpTransformRect;
                    boundingRect.set(dirty);
                    Matrix transformMatrix;
                    if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
                        Transformation t = attachInfo.mTmpTransformation;
                        boolean transformed = getChildStaticTransformation(child, t);
                        if (transformed) {
                            transformMatrix = attachInfo.mTmpMatrix;
                            transformMatrix.set(t.getMatrix());
                            if (!childMatrix.isIdentity()) {
                                transformMatrix.preConcat(childMatrix);
                            }
                        } else {
                            transformMatrix = childMatrix;
                        }
                    } else {
                        transformMatrix = childMatrix;
                    }
                    transformMatrix.mapRect(boundingRect);
                    dirty.set((int) Math.floor(boundingRect.left),
                            (int) Math.floor(boundingRect.top),
                            (int) Math.ceil(boundingRect.right),
                            (int) Math.ceil(boundingRect.bottom));
                }
                // 循环向上遍历到视图树结构的根节点ViewRootImpl
                do {
                    View view = null;
                    if (parent instanceof View) {
                        view = (View) parent;
                    }
    
                    if (drawAnimation) {
                        if (view != null) {
                            view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                        } else if (parent instanceof ViewRootImpl) {
                            ((ViewRootImpl) parent).mIsAnimating = true;
                        }
                    }
    
                    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
                    // flag coming from the child that initiated the invalidate
                    if (view != null) {
                        if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
                            view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
                        }
                    }
    
                    parent = parent.invalidateChildInParent(location, dirty);
                    if (view != null) {
                        // Account for transform on current parent
                        Matrix m = view.getMatrix();
                        if (!m.isIdentity()) {
                            RectF boundingRect = attachInfo.mTmpTransformRect;
                            boundingRect.set(dirty);
                            m.mapRect(boundingRect);
                            dirty.set((int) Math.floor(boundingRect.left),
                                    (int) Math.floor(boundingRect.top),
                                    (int) Math.ceil(boundingRect.right),
                                    (int) Math.ceil(boundingRect.bottom));
                        }
                    }
                } while (parent != null);
            }
        }
    
    1.3 简单分析下流程吧,如果是硬件加速,会直接进入绘制流程(其实最终走向也是一样的),否则会通过一系列计算出脏区,遍历到视图树的根节点,重点看这行代码parent = parent.invalidateChildInParent(location, dirty);,视图树的根节点,也就是ViewRootImpl,所以直接看ViewRootImpl.invalidateChildInParent方法。绘制细节不是本文重点分析,重点分析的是整个渲染流程,细节部门自行查看。
    <ViewRootImpl.java>
        @Override
        public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
            checkThread();// 检测线程
            if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
    
            if (dirty == null) {// 脏区为null,全局重绘
                invalidate();
                return null;
            } else if (dirty.isEmpty() && !mIsAnimating) {
                return null;
            }
            // 加入滚动,平移参数计算出真正的脏区
            if (mCurScrollY != 0 || mTranslator != null) {
                mTempRect.set(dirty);
                dirty = mTempRect;
                if (mCurScrollY != 0) {
                    dirty.offset(0, -mCurScrollY);
                }
                if (mTranslator != null) {
                    mTranslator.translateRectInAppWindowToScreen(dirty);
                }
                if (mAttachInfo.mScalingRequired) {
                    dirty.inset(-1, -1);
                }
            }
            // 脏区重绘
            invalidateRectOnScreen(dirty);
            // 当前已经是根节点,return null退出ViewGroup遍历循环
            return null;
        }
    
        // 全局重绘 
        @UnsupportedAppUsage
        void invalidate() {
            mDirty.set(0, 0, mWidth, mHeight);
            if (!mWillDrawSoon) {
                scheduleTraversals();// 这就是渲染流程的真正开始
            }
        }
    
        // 脏区重绘(高效)
        private void invalidateRectOnScreen(Rect dirty) {
            final Rect localDirty = mDirty;
    
            // Add the new dirty rect to the current one
            localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
            // Intersect with the bounds of the window to skip
            // updates that lie outside of the visible region
            final float appScale = mAttachInfo.mApplicationScale;
            final boolean intersected = localDirty.intersect(0, 0,
                    (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
            if (!intersected) {
                localDirty.setEmpty();
            }
            if (!mWillDrawSoon && (intersected || mIsAnimating)) {
                scheduleTraversals();// 这就是渲染流程的真正开始
            }
        }
    
    1.4 上面的流程很明了,不论是全局刷新还是局部刷新,最终都是走向scheduleTraversals,所以可以说scheduleTraversals方法是上层发起重绘的起点。(硬件加速其实也是一样走向scheduleTraversals方法,只是在底层绘制上有所区别)
    2.View.requestLayout流程基本一致,也是通过向上遍历,最终调用视图树根节点的``方法:
    <ViewRootImpl.java>
        @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                checkThread();// 检测线程
                mLayoutRequested = true;
                scheduleTraversals();
            }
        }
    
    3.到这里先暂停下,补充个知识点,面试常见的坑:为何只能主线程刷新UI?
    • 上面不论是invalidata还是requestLayout,方法内部的第一行代码都是checkThread,这里面就有常见的异常打印:
        void checkThread() {
            if (mThread != Thread.currentThread()) {
                // 熟悉的文字
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
        }
    
    • Android禁止主线程刷新UI,其实就是在ViewRootImpl中所有涉及UI操作方法中判断非当前线程主动抛出异常而已,典型的强制措施(其实也是为了能保证主线程的同步性可靠性,要是大家都在子线程刷新UI,最终合成渲染图层岂不是画面凌乱了?)
    • 所以本质上通过反射,或者在ViewRootImpl未初始化前,都是可以在子线程刷新UI。这也是为何在Activity.onCreate方法中可以子线程刷新UI不会崩溃的原因。
    4.题外话说多了,现在正式开始分析渲染流程:
    4.1 首先介绍一下Traversal相关几个方法:scheduleTraversals -> doTraversal -> performTraversals,这几个方法的执行顺序是schedule -> do -> perform,可以理解为计划准备阶段->准备执行阶段->完成阶段
    4.2 scheduleTraversals分析:
    <ViewRootImpl.java>
        @UnsupportedAppUsage
        void scheduleTraversals() {
            if (!mTraversalScheduled) {// 这里会限制重入,一般情况16ms你不论调用多少次invalidate或者requestLayout,最终效果都是一样
                mTraversalScheduled = true;// 重入限制
                // 消息屏障,异步消息,之前handler章节分析过
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();// 发送消息屏障,保证优先级
                mChoreographer.postCallback(// 请求下一次Vsync信号
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                // 相关通知回调
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    
        // 请求Vsync的时候传递了这个参数mTraversalRunnable
        final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
        // 非常简单的一个runnable
        final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();// 执行阶段,下面分析
            }
        }
    
    • 内部通过一个mTraversalScheduled变量限制重入,所以一般情况16ms你不论调用多少次invalidate或者requestLayout,最终效果都是一样,并且你调用之后并不是立即就执行重绘,后面分析。这里还涉及到异步消息,之前分析过,不具体分析,简单来说就是往消息队列插入一条异步消息作为屏障,插入屏障之后消息队列的同步消息停止执行,直到该消息屏障移除后才恢复,主要就是为了保证优先级,毕竟交互响应是优先级最高的。这里还涉及到Choreographer编舞者的角色,主要是解决帧率不同步,掉帧问题,非本文重点,本文只分析下其内部对于vsync请求流程和回调时机。
    <Choreographer.java>
        // 请求Vsync信号,postCallback最终会走到这里
        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;
                // 将runnable添加到缓存队列
                mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
                if (dueTime <= now) {
                    // 分支1:需要立即执行回调
                    scheduleFrameLocked(now);
                } else {
                    // 分支2:还未到需要的执行时间,在指定的时间发送异步消息,保证回调执行的优先级
                    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.");
                    }
    
                    // If running on the Looper thread, then schedule the vsync immediately,
                    // otherwise post a message to schedule the vsync from the UI thread
                    // as soon as possible.
                    if (isRunningOnLooperThreadLocked()) {// 当前如果是主线程
                        scheduleVsyncLocked();// vsync准备阶段
                    } else {// 场景1:线程切换,直接插入一条消息到队头
                        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageAtFrontOfQueue(msg);
                    }
                } else { // 场景2:发送消息
                    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);
                }
            }
        }
    
        // 上面的mHandler是FrameHandler,属于主线程handler,具体实例化过程不分析
        private final class FrameHandler extends Handler {
            public FrameHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_DO_FRAME:// scheduleFrameLocked的场景2
                        doFrame(System.nanoTime(), 0);
                        break;
                    case MSG_DO_SCHEDULE_VSYNC:// scheduleFrameLocked的场景1
                        doScheduleVsync();// 最终还是走到scheduleVsyncLocked,场景1只是多了一步线程切换
                        break;
                    case MSG_DO_SCHEDULE_CALLBACK:// postCallbackDelayedInternal的分支2
                        doScheduleCallback(msg.arg1);
                        break;
                }
            }
        }
    
        // 使用vsync的场景1
        void doScheduleVsync() {
            synchronized (mLock) {
                if (mFrameScheduled) {
                    scheduleVsyncLocked();
                }
            }
        }
    
        // 最终流程和postCallbackDelayedInternal的分支1一致,只是消息延迟点执行而已
        void doScheduleCallback(int callbackType) {
            synchronized (mLock) {
                if (!mFrameScheduled) {
                    final long now = SystemClock.uptimeMillis();
                    if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                        scheduleFrameLocked(now);
                    }
                }
            }
        }
    
        // 请求vsync
        @UnsupportedAppUsage
        private void scheduleVsyncLocked() {
            mDisplayEventReceiver.scheduleVsync();
        }
    
        // doFrame关键方法
        // 1.非使用vsync会直接执行该方法,也就是直接出发重绘回调
        // 2.使用vsync会等下一次vsync信号来到时,触发重绘回调
        @UnsupportedAppUsage
        void doFrame(long frameTimeNanos, int frame) {
            ...// 后面分析
        }
    
    • ViewRootImpl请求vsync信号的时候,会传入一个runnable消息,Choreographer将这个消息存放到队列中,并且根据当前时间,决定是立即安排vsync计划还是延时(scheduleFrameLocked(now)和MSG_DO_SCHEDULE_CALLBACK),本质上最终调用scheduleFrameLocked(long now)方法。
    • scheduleFrameLocked(long now)中有两种分支,一种使用vsync机制,一种非使用vsync机制。他们的区别就是使用该同步机制,会在下一次vsync信号到来时进行刷新,否则立即刷新(doFrame方法)。下面分析下请求vsync的流程:
    <Choreographer.java>
        // 请求vsync
        @UnsupportedAppUsage
        private void scheduleVsyncLocked() {
            mDisplayEventReceiver.scheduleVsync();// 请求vsync信号
        }
    
    <DisplayEventReceiver.java>
        public void scheduleVsync() {
            if (mReceiverPtr == 0) {
                Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                        + "receiver has already been disposed.");
            } else {
                // native请求vsync信号,当底层发送vsync信号时,java层就能接收到通知
                nativeScheduleVsync(mReceiverPtr);
            }
        }
    
        // 当底层发送vsync信号时会调用这个java方法
        // Called from native code.
        @SuppressWarnings("unused")
        @UnsupportedAppUsage
        private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
            // 回调onVsync方法
            onVsync(timestampNanos, physicalDisplayId, frame);
        }
    
    • 其实请求vsync过程很简单,就是通过jni向底层注册一个回调(构造内会保存c++层Receiver引用的指针地址),底层发送vsync时候,反向调用java方法(onVsync)通知上层。DisplayEventReceiver是一个抽象类,在Choreographer中可以找到一个具体实现的内部类FrameDisplayEventReceiver:
    <Choreographer.java>
        private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable {
            private boolean mHavePendingVsync;
            private long mTimestampNanos;
            private int mFrame;
    
            public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
                super(looper, vsyncSource);
            }
    
            // 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) {
                // 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;
                // 发送异步消息,因为自己实现了runnable,所以是把自己当成消息发送出去,看下面的run方法
                Message msg = Message.obtain(mHandler, this);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
            }
    
            @Override
            public void run() {
                // 执行doFrame方法进行重绘
                mHavePendingVsync = false;
                doFrame(mTimestampNanos, mFrame);
            }
        }
    
    • 下面就到关键的方法doFrame了,这里你也会看到很多常见的log打印信息:
        @UnsupportedAppUsage
        void doFrame(long frameTimeNanos, int frame) {
            final long startNanos;
            synchronized (mLock) {// 上锁
                if (!mFrameScheduled) {// 这个标记位就是最初scheduleFrameLocked开始限制重入那个
                    return; // no work to do
                }
    
                if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
                    mDebugPrintNextFrameTimeDelta = false;
                    Log.d(TAG, "Frame time delta: "
                            + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
                }
    
                long intendedFrameTimeNanos = frameTimeNanos;// 本次vsync时间
                startNanos = System.nanoTime();// 开始执行doFrame时间
                final long jitterNanos = startNanos - frameTimeNanos;// jitterNanos = doFrame - Vsync 的时间差
                // mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
                if (jitterNanos >= mFrameIntervalNanos) {// 假设帧率为60fps,mFrameIntervalNanos为通常所说的16ms
                    final long skippedFrames = jitterNanos / mFrameIntervalNanos;// 计算跳帧数
                    if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {// >30fps
                        // 熟悉的掉帧打印信息
                        Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                                + "The application may be doing too much work on its main thread.");
                    }
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;// doFrame延迟n个周期后取余的时间
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                                + "which is more than the frame interval of "
                                + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                + "Skipping " + skippedFrames + " frames and setting frame "
                                + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                    }
                    // 修正vsync的到来时间
                    frameTimeNanos = startNanos - lastFrameOffset;
                    // lastFrameOffset = jitterNanos % mFrameIntervalNanos
                    // frameTimeNanos = startNanos - lastFrameOffset = startNanos - (jitterNanos % 16) = startNanos - (startNanos - frameTimeNanos) % 16
                    // 所以 frameTimeNanos = 当前doFrame时间之前最近的一个vsync时间
                }
                // 避免下一帧提前渲染,如果本次vsync执行doFrame比上一帧计划的提交时间早,则将本帧放到下一个vsync进行渲染
                // mLastFrameTimeNanos在修正过程可能出现这种场景
                // 提前渲染就会出现画面重叠重影现象
                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.");
                    }
                    // 重新请求下一次vsync信号,此刻mFrameScheduled没有重置false,外部调用的scheduleFrameLocked(now)不再执行,也就是此时外部postCallback也是无效的
                    // 请求下一次vsync信号->doFrame,如果还不满足条件,重复如此
                    scheduleVsyncLocked();
                    return;
                }
                // 默认1,不用管
                if (mFPSDivisor > 1) {
                    long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                    if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                        scheduleVsyncLocked();
                        return;
                    }
                }
                // 记录当前帧的原始vsync时间-修正后的vsync时间
                mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
                mFrameScheduled = false;// 重置标记位,可以再次进入scheduleFrameLocked
                // 记录上一次vsync的时间
                mLastFrameTimeNanos = frameTimeNanos;
            }
            // 开始执行各种callback
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
                AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
                // 输入
                mFrameInfo.markInputHandlingStart();
                doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
                // 动画
                mFrameInfo.markAnimationsStart();
                doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
                doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
                // 遍历:measure,layout,draw
                mFrameInfo.markPerformTraversalsStart();
                doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
                // 遍历完成提交,修复下一帧的提交时间,保证和vsync节奏同步
                doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
            } 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.");
            }
        }
    
    • 上面可以看到接收到了vsync信号后会先判断是否掉帧(执行doFrame时间比Vsync的时间延迟),打印出掉帧信息,再进行渲染刷新,之前ViewRootImpl.scheduleTraversals方法中通过mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)明确这个callback的类型Choreographer.CALLBACK_TRAVERSAL,所以doFrame中我们重点分析该类型的回调:
    <Choreographer.java>
    
        void doFrame(long frameTimeNanos, int frame) {   
            ......
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
            ......
        }
    
        void doCallbacks(int callbackType, long frameTimeNanos) {
            CallbackRecord callbacks;// 单链表结构
            synchronized (mLock) {
                // We use "now" to determine when callbacks become due because it's possible
                // for earlier processing phases in a frame to post callbacks that should run
                // in a following phase, such as an input event that causes an animation to start.
                final long now = System.nanoTime();
                // 从队列中取出callback链表,包含我们之前scheduleTraversals传进来的callback
                // CallbackQueue为一个子元素为链表的数组队列,里面每一种callback类型都是一个CallbackRecord的单链表
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(// 取出执行时间在当前时间之前的callback
                        now / TimeUtils.NANOS_PER_MS);
                if (callbacks == null) {
                    return;
                }
                mCallbacksRunning = true;
    
                // Update the frame time if necessary when committing the frame.
                // We only update the frame time if we are more than 2 frames late reaching
                // the commit phase.  This ensures that the frame time which is observed by the
                // callbacks will always increase from one frame to the next and never repeat.
                // We never want the next frame's starting frame time to end up being less than
                // or equal to the previous frame's commit frame time.  Keep in mind that the
                // next frame has most likely already been scheduled by now so we play it
                // safe by ensuring the commit time is always at least one frame behind.
                if (callbackType == Choreographer.CALLBACK_COMMIT) {
                    // 进入这个分支后,now = 执行完动画,绘制一系列操作之后的当前时间
                    // 提交刷新,修正时间,同步vsync的节奏
                    final long jitterNanos = now - frameTimeNanos;
                    Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                    if (jitterNanos >= 2 * mFrameIntervalNanos) {// > 2*16 = 32ms(60fps为例)
                        final long lastFrameOffset = jitterNanos % mFrameIntervalNanos// frameTimeNanos = startNanos - lastFrameOffset,now是执行玩measure-layout-draw的时间
                                + mFrameIntervalNanos;// mFrameIntervalNanos=16ms
                        if (DEBUG_JANK) {
                            Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                                    + " ms which is more than twice the frame interval of "
                                    + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                    + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                                    + " ms in the past.");
                            mDebugPrintNextFrameTimeDelta = true;
                        }
                        // 修正时间 mLastFrameTimeNanos = frameTimeNanos = 从now往前最近的一个vsync时间
                        frameTimeNanos = now - lastFrameOffset;
                        mLastFrameTimeNanos = frameTimeNanos;
                    }
                }
                // 大致总结下:在一帧处理过程,如果超过了n>=2个vsync周期,则会在接下来n个vsync周期中不再处理任何帧,下一帧会在n个周期后对齐vsync信号时开始处理,相当于中途抛弃n帧画面,达到尽可能帧率平稳,与vsync同步
            }
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
                // 循环执行callback的run方法
                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));
                    }
                    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);
            }
        }
    
    • 逻辑很简单,从缓存队列取出之前传入的callback,调用其run方法,也就是:
    <ViewRootImpl.java>
        final class TraversalRunnable implements Runnable {
            // 就是这个run方法
            @Override
            public void run() {
                doTraversal();
            }
        }
    
    4.2 至此已经分析完了scheduleTraversals请求vsync的过程,下面开始分析渲染刷新doTraversal->performTraversal:
    <ViewRootImpl.java>
        void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;// 走到这里,放开了重入,这个时候外部调用invalidate之类请求重绘才会生效
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);// 移除消息屏障,主线程同步消息恢复运转
                if (mProfile) {
                    Debug.startMethodTracing("ViewAncestor");
                }
                // 核心方法,View的layout,measure,draw相关方法都是在这里面执行的,简单描述下流程,细节不展开了(否则没玩没了,这个方法巨长,800多行代码)
                performTraversals();
    
                if (mProfile) {
                    Debug.stopMethodTracing();
                    mProfile = false;
                }
            }
        }
    
        private void performTraversals() {
            // cache mView since it is used so much below...
            final View host = mView;// 其实就是DecorView
            ......
    
            if (host == null || !mAdded)// DecorView没初始化和添加到window,直接return
                return;
    
            mIsInTraversal = true;// 标记正在执行遍历
            mWillDrawSoon = true;// 标记立即绘制
            ......
            if (mFirst) {// 初始化第一次的时候
                ......
                // OnAttachedToWindow回调
                host.dispatchAttachedToWindow(mAttachInfo, 0);
                mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
                dispatchApplyInsets(host);
            } else {
                ......
            }
    
            ......
    
            // Execute enqueued actions on every traversal in case a detached view enqueued an action
            // 这个在之前View.post原理的文章中已经分析过
            getRunQueue().executeActions(mAttachInfo.mHandler);
    
            ......
    
            if (mFirst || windowShouldResize || insetsChanged ||
                    viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
                ......
                if (!mStopped || mReportNextDraw) {
                    boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                            (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                    if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                            || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                            updatedConfiguration) {
                        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
    
                        ......
    
                        // 开始view的测量
                         // Ask host how big it wants to be
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    
                        // Implementation of weights from WindowManager.LayoutParams
                        // We just grow the dimensions as needed and re-measure if
                        // needs be
                        int width = host.getMeasuredWidth();
                        int height = host.getMeasuredHeight();
                        boolean measureAgain = false;
    
                        if (lp.horizontalWeight > 0.0f) {
                            width += (int) ((mWidth - width) * lp.horizontalWeight);
                            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                                    MeasureSpec.EXACTLY);
                            measureAgain = true;
                        }
                        if (lp.verticalWeight > 0.0f) {
                            height += (int) ((mHeight - height) * lp.verticalWeight);
                            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                                    MeasureSpec.EXACTLY);
                            measureAgain = true;
                        }
    
                        if (measureAgain) {
                            if (DEBUG_LAYOUT) Log.v(mTag,
                                    "And hey let's measure once more: width=" + width
                                    + " height=" + height);
                            // 需要多次测量的话,再次进行view的测量,所以有的viewgroup会测量两次
                            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                        }
    
                        layoutRequested = true;
                    }
                }
            } else {
                // Not the first pass and no window/insets/visibility change but the window
                // may have moved and we need check that and if so to update the left and right
                // in the attach info. We translate only the window frame since on window move
                // the window manager tells us only for the new frame but the insets are the
                // same and we do not want to translate them more than once.
                maybeHandleWindowMove(frame);
            }
    
            if (surfaceSizeChanged) {
                updateBoundsSurface();
            }
    
            final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
            boolean triggerGlobalLayoutListener = didLayout
                    || mAttachInfo.mRecomputeGlobalAttributes;
            if (didLayout) {
                // ViewGroup进行布局子View
                performLayout(lp, mWidth, mHeight);
    
                ......
            }
    
            ......
    
            boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
            // 不可见或者正在绘制,就不需要绘制了
            if (!cancelDraw) {
                if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                    for (int i = 0; i < mPendingTransitions.size(); ++i) {
                        mPendingTransitions.get(i).startChangingAnimations();
                    }
                    mPendingTransitions.clear();
                }
                // 遍历绘制
                performDraw();
            } else {
                ......
            }
    
            mIsInTraversal = false;
        }
    
    • 总结一下,performTraversals中调用了performMeasure->performLayout->performDraw View的三大流程,三大流程内部都是通过遍历子view,遍历调用我们熟悉的onMeasure->onLayout->onDraw回调,源码的逻辑很清晰,在此不分析了。
    4.3 至此,doTraversalperformTraversals分析完了。我们应用中的draw之类的api调用其实都是在操作底层skia引擎对应的SkiaCanvas画布,在framework层对应存在一块buffer保存图元数据,最终通过SurfaceFlinger进行图层合并处理,以及颜色矩阵运算(Android原生的护眼模式就是这部分操作的,在最终渲染画面前通过颜色矩阵运算改变显示输出色温)等一系列操作,然后提交给GPU处理渲染到屏幕硬件上,SurfaceFlinger是系统的图形管理服务(纯c++服务,不像AMS,PMS,WMS),核心流程是下面几个方法,应用开发可以不需要过多关注,感兴趣的自行阅读。
    <SurfaceFlinger.cpp>
    // SurfaceFlinge中图层渲染合成关键流程方法
    void SurfaceFlinger::handleMessageRefresh() {
        ATRACE_CALL();
        preComposition();
        rebuildLayerStacks();
        setUpHWComposer();
        doDebugFlashRegions();
        doComposition();
        postComposition();
    }
    
    最后用一张图总结下整个流程:
    渲染流程.png

    相关文章

      网友评论

        本文标题:Android渲染绘制流程

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