探索Android渲染

保持好奇心,保持激情,不被环境所同化

这是大学毕业的时候,一位挚友、学长送我的一句话。转眼即将毕业两年,很庆幸自己能够一直践行着这句话,伴随着的是满满的收获与成长。更让我庆幸的是,工作中遇到了非常多经验丰富、实力雄厚的前辈同事,得益于他们的指点、教诲,自己满足好奇心的能力也在逐步提高。

这篇文章主要分享我是如何满足自己对于Android渲染的好奇心。

Android对于View的绘制主要会经过measure、layout、draw几个步骤,那么就从measure入手,看看Android是如何调用到View的onMeasure方法的。

以上是我在一个Android自定义View的onMeasure方法中挂的断点,可以看到是通过MainThread的Lopper发起,MainThread的Handler处理Callback,然后调用到Choreographer,执行一系列的doFrame -> doCallbacks -> doTraversal -> performTraversals -> measureHierarchy。大概了解流程之后,接下来深入Choreographer、ViewRootImpl内部了解。

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        ...

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            ...
            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }

以上是Choreographer第一步doFrame调用位置的关键代码,可以看到FrameDisplayEventReceiver实现了Runnable接口,Runnale的run方法中会调用doFrame方法,那么接下来需要确认这个Runnable是如何放入Lopper的消息队列的?不难发现,就在FrameDisplayEventReceiver的onVsync方法中,Handler通过sendMessage传入了msg,msg的obtain方法中传入了此Runnable。

通过在onVsync中挂断点,可以发现是通过DisplayEventReciever的dispatchVsync调用的,同时也发现FrameDisplayEventReceiver也是继承自DisplayEventReciever,然后实现了onVsync方法

/**
 * Provides a low-level mechanism for an application to receive display events
 * such as vertical sync.
 *
 * The display event receive is NOT thread safe.  Moreover, its methods must only
 * be called on the Looper thread to which it is attached.
 *
 * @hide
 */
public abstract class DisplayEventReceiver {
    ...

    private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
            MessageQueue messageQueue, int vsyncSource);
    private static native void nativeDispose(long receiverPtr);
    @FastNative
    private static native void nativeScheduleVsync(long receiverPtr);

    ...

    /**
     * Called when a vertical sync pulse is received.
     * The recipient should render a frame and then call {@link #scheduleVsync}
     * to schedule the next vertical sync pulse.
     *
     * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
     * timebase.
     * @param builtInDisplayId The surface flinger built-in display id such as
     * {@link SurfaceControl#BUILT_IN_DISPLAY_ID_MAIN}.
     * @param frame The frame number.  Increases by one for each vertical sync interval.
     */
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
    }

    /**
     * Schedules a single vertical sync pulse to be delivered when the next
     * display frame begins.
     */
    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 {
            nativeScheduleVsync(mReceiverPtr);
        }
    }

    // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
        onVsync(timestampNanos, builtInDisplayId, frame);
    }
}

以上是DisplayEventReceiver的关键代码,可以看到主要是接收Vsync信号,Vsync(即V-Sync垂直同步),就是一个信号源。假设你的屏幕是60FPS的话,那意味着每隔1s,屏幕就有60次中断信号产生,即每隔16.666ms,就会有一次中断信号产生,也就是我们经常听说的Android帧率绘制。关于Vsync是如何产生?如何从Hardware传到SurfaceFlinger?如何从SurfaceFlinger传到这个位置?这篇文章不多做探索,对此好奇的朋友可以自行满足自己。这里我比较好奇的是,Vsync信号一直产生,Android是如何做到在有需要的时候监听Vsync信号并处理绘制渲染变化呢?

以上是我触摸手机屏幕并滑动列表的时候在DisplayEventReceiver中的scheduleVsync方法中挂的断点,可以看到整个调用栈是InputEventReceiver.dispatchBatchedInputEventPending -> ViewRootImpl.scheduleConsumeBatchedInput -> Choreographer.postCallback -> ... -> Choreographer.scheduleVsyncLocked -> DisplayEventReceiver.scheduleVsync

/**
     * Posts a callback to run on the next frame.
     * ...
     */
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }

    /**
     * Posts a callback to run on the next frame after the specified delay.
     * ...
     */
    public void postCallbackDelayed(int callbackType,
            Runnable action, Object token, long delayMillis) {
        ...

        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }

    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        ...

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            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) {
                ...
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                ...
            }
        }
    }

    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

以上是Choreographer中的相关代码,当有input等操作导致Application界面需要更新的时候,Android Framework会通过Choreographer postCallback通知DisplayEventReceiver要开始监听处理Vsync信号了,那么下一个Vsync信号到来时就会调用FrameDisplayEventReceiver的onVsync方法,至此,就成功与之前分析到的doFrame流程接通了。

关于Android渲染,以上探索了Android Framework是如何触发开始渲染的。前段时间结合Android系统源码,系统阅读了罗升阳老师相关系列文章,主要讲到MainThread如何构建DisplayList,RenderThread如何将DisplayList转化为SurfaceFlinger可以接收的FrameBuffer,以及相关线程如何协作、同步等,推荐给各位

blog.csdn.net/luoshengy

blog.csdn.net/luoshengy

blog.csdn.net/luoshengy

后续我会继续深入探索研究这一部分,欢迎感兴趣的朋友一起探讨。也希望自己满足好奇心的能力能够进一步增强,并且在其他能力方面逐渐有所突破!

发布于 2018-06-03

文章被以下专栏收录