• Android帧绘制流程深度解析 (二)


    书接上回:Android帧绘制流程深度解析 (一)请添加图片描述

    5、 dispatchVsync:

    在请求Vsync以后,choreographer会等待Vsync的到来,在Vsync信号到来后,会触发dispatchVsync函数,从而调用onVsync方法:

    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
            VsyncEventData vsyncEventData) {
        onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
    }
    

    不过这里需要注意的是,DisplayEventReciever这个类中就有一个onVsync方法,不过这个onVsync方法是空的,所以其实这里是调用的是FrameDisplayEventReciever中的onVsync方法。
    FrameDisplayEventReciever类是Choreographer类的一个内部类,其继承自DisplayEventReciever类,所以就有了onVsync方法。其实这里从头到尾调用的都是FrameDisplayEventReciever类的方法,只是因为该类没有dispatchVsync方法,所以才会调用到了DisplayEventReciever类。

    6、 onVsync:

    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.preferredFrameTimeline().vsyncId);
            }
            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;
            Message msg = Message.obtain(mHandler, this);//(1)生成消息,并发送给mHandler
            msg.setAsynchronous(true);//设置为异步消息
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);//发送消息
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    

    (1)处可见,生成一个消息,其调用的函数是Message中的这个方法:

    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;
    
        return m;
    }
    

    第一个参数是目标handler,第二个参数是callback;这里涉及到消息机制中一个知识点就是在dispatchMessage的时候,会优先查看消息的runnable内容,再到

    handler的callback,如果前两个存在的话,就不会去再调用handleMessage流程了。
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);//(2)
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    	所以上面(1)处的功能就是构建一个发送给mHandler的,callback为当前对象的消息,当前对象就是FrameDisplayEventReceiver这个类的对象,所以再mHandler端读取消息时,会做什么呢? 当然是执行上面(2)处的功能,而且FrameDisplayEventReciever类也实现了Runnable,所以这里就是要去执行FrameDisplayEventReciever类对象的run方法了。
    	而在消息发送时,具体发送到哪呢?根据mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);这行代码可以看出,调用的是mHandler的发送函数。所以跟到Handler类中的sendMessageAtTime函数:
    
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    	从老方法中可以看出,最后就是将消息入队到队列queue中,而queue就是当前Handler类的对象mHandler的成员变量mQueue。
    	再回到Choreographer类,mHandler的定义是FrameHandler类的成员变量;而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:
                    doFrame(System.nanoTime(), 0, new DisplayEventReceiver.VsyncEventData());
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }
    

    看到这里发现FrameHandler不过是重写了handleMessage方法而已,其实对我们上面发送的消息都没影响的。然后还有就是要找到mHandler对应的mQueue是什么。
    还记得之前说的消息机制吗?消息队列是在哪初始化的?对了就是Looper的初始化方法中初始化的,而这里的,然后Handler类构建对象时,会将Looper的消息队列赋值给自己。所以能将Handler和Looper关联上。而Looper又是跟当前线程相关联的,所以这里的消息队列就是Choreographer所在的线程的消息队列了。

    7、 run:

    在接受到Vsync信号触发的帧绘制的消息后,主线程就会执行这个Runnable的run方法,也就是FrameDisplayEventReciever中的run方法;该方法很简单,就一个功能,就是doFrame;

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

    不过整个doFrame的逻辑还是相当复杂的。

    8、 doFrame:

    	 void doFrame(long frameTimeNanos, int frame,
            DisplayEventReceiver.VsyncEventData vsyncEventData) {
        final long startNanos;
        final long frameIntervalNanos = vsyncEventData.frameInterval;
        try {
            FrameData frameData = new FrameData(frameTimeNanos, vsyncEventData);
            synchronized (mLock) {
                if (!mFrameScheduled) {//在scheduleFrameLocked处置为true,会在本函数中置为false,正常情况下在这里是为true的
                    traceMessage("Frame not scheduled");
                    return; // no work to do
                }
    
                long intendedFrameTimeNanos = frameTimeNanos;//记录传入的帧绘制时间
                startNanos = System.nanoTime();//获取当前时间
                final long jitterNanos = startNanos - frameTimeNanos;//当前时间减去当前帧应该绘制的时刻,如果出现掉帧,该值会基于掉帧时间越来越大
                if (jitterNanos >= frameIntervalNanos) {// frameIntervalNanos是单帧绘制的时间
                    long lastFrameOffset = 0;//初始化掉帧后的偏移时间量
                    if (frameIntervalNanos == 0) {
                        Log.i(TAG, "Vsync data empty due to timeout");
                    } else {
                        lastFrameOffset = jitterNanos % frameIntervalNanos;//掉帧时间偏移量
                        final long skippedFrames = jitterNanos / frameIntervalNanos;//掉帧数量
                        if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {//掉了30帧以上
                            Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                                    + "The application may be doing too much work on its main "
                                    + "thread.");
                        }
                        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;//将帧的理论起始时间进行更新。
                    frameData.updateFrameData(frameTimeNanos);
                }
    
                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.preferredFrameTimeline().vsyncId,
                        vsyncEventData.preferredFrameTimeline().deadline, startNanos,
                        vsyncEventData.frameInterval);
                mFrameScheduled = false;//复位mFrameScheduled
                mLastFrameTimeNanos = frameTimeNanos;//将当前帧的消息作为历史帧进行记录了
                mLastFrameIntervalNanos = frameIntervalNanos;
                mLastVsyncEventData = vsyncEventData;
            }
    
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameData, frameIntervalNanos);//处理输入事件
    
            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameData, frameIntervalNanos);//处理动画
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameData,
                    frameIntervalNanos);
    
            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameData, frameIntervalNanos);//处理界面的重绘
    
            doCallbacks(Choreographer.CALLBACK_COMMIT, frameData, 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.");
        }
    }
    

    从上面的代码看内容比较多,其实总结起来就是两件事:1、记录当前帧的信息,将其作为历史帧;2、按需执行五个callback。
    这里的callback是采用一个二维数组进行存储的,数组为:
    private final CallbackQueue[] mCallbackQueues;
    这个数组在Choreographer的构造函数中初始化:
    mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
    for (int i = 0; i <= CALLBACK_LAST; i++) {
    mCallbackQueues[i] = new CallbackQueue();
    }

    其中CALLBACK_LAST值为4,所以就是一个5*n的数组,数组存储5类回调分别为:

    public static final int CALLBACK_INPUT = 0;
    public static final int CALLBACK_ANIMATION = 1;
    public static final int CALLBACK_INSETS_ANIMATION = 2;
    public static final int CALLBACK_TRAVERSAL = 3;
    public static final int CALLBACK_COMMIT = 4;
    

    其中2CALLBACK_INSETS_ANIMATIO的功能我不是很了解,而本次讲的界面刷新流程,就是第三类CALLBACK_TRAVERSAL了。也就是在postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis)方法中的这里,会根据type将action入队,然后等待后面的doFrame方法将action取出来再执行了。
    mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

    9、 总结:

    整个帧绘制的流程还是比较复杂的,但是我也在这个过程中,对消息机制等知识点,在流程中进行更加详细的分析和讲解。其中可能也存在理解不到位的地方,还希望大家多多指正。

  • 相关阅读:
    MyBatis扩展之事务和缓存以及ORM
    C语言之动态内存管理篇(1)
    MybatisX快速开发插件模版扩展
    nslookup命令的常见用法
    spring cloud alibaba 集成seata
    C++笔记 - - list的模拟实现和使用
    M0007 四则运算
    Unity中从3D模型资产中批量提取材质
    2022年最新Go面试题汇总
    认识微服务
  • 原文地址:https://blog.csdn.net/weixin_39702448/article/details/139639416