Choreographer 源码解析

Choreographer 源码解析

1.构造方法说明

  • 核心类,参数了解
  • callback类型了解
	private Choreographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        //接收处理消息,详细见第2章
        mHandler = new FrameHandler(looper);
        //用来接收垂直同步脉冲信号(VSync),详细见第3章
        mDisplayEventReceiver = USE_VSYNC
                ? new FrameDisplayEventReceiver(looper, vsyncSource)
                : null;
        //上一帧的时间,单位纳秒
        mLastFrameTimeNanos = Long.MIN_VALUE;

       //渲染间隔(1s/屏幕刷新率)
        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

       //初始化队列(注释1)
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
        // b/68769804: For low FPS experiments.
        setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
    }
1.1 注释1处解释
1.1.1 疑问

为什么这个数组的初始化size是5,且直接初始化了5个元素,为什么不使用懒加载的方式?

这5个元素分别代表不同的事件类型,具体如下。数字代表对应的在数组中的index。

//Callback type: Input callback. Runs first.
public static final int CALLBACK_INPUT = 0;

//Callback type: Animation callback. Runs before CALLBACK_INSETS_ANIMATION.
public static final int CALLBACK_ANIMATION = 1;

//Callback type: Animation callback to handle inset updates. This is separate from CALLBACK_ANIMATION as we need to "gather" all inset animation updates via WindowInsetsAnimationController.setInsetsAndAlpha(Insets, float, float) for multiple ongoing animations but then update the whole view system with a single callback to View.dispatchWindowInsetsAnimationProgress that contains all the combined updated insets.
//Both input and animation may change insets, so we need to run this after these callbacks, but before traversals.
//Runs before traversals.
public static final int CALLBACK_INSETS_ANIMATION = 2;

//Callback type: Traversal callback. Handles layout and draw. Runs after all other asynchronous messages have been handled.
public static final int CALLBACK_TRAVERSAL = 3;

//Callback type: Commit callback. Handles post-draw operations for the frame. Runs after traversal completes. The frame time reported during this callback may be updated to reflect delays that occurred while traversals were in progress in case heavy layout operations caused some frames to be skipped. The frame time reported during this callback provides a better estimate of the start time of the frame in which animations (and other updates to the view hierarchy state) actually took effect.
public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;

之所以直接初始化了5个,有个猜测,可能Android认为页面事件是最高优先级的时间(不然也不会发屏障消息把其他消息阻塞住了),所以这里要尽可能的减少消耗。

1.1.2 举例说明

比如ViewRootImpl中发送一个CALLBACK_TRAVERSAL类型的callback

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

2.FrameHandler

了解FrameHandler做了什么,如何通信

	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:
                //请求VSync信号
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                //请求执行callback
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

FrameDisplayEventReceiver

  • 了解FrameDisplayEventReceiver做了,整个VSync信号处理流程
  • 了解如何保证渲染事件高优先级处理的
    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
        private VsyncEventData mLastVsyncEventData = new VsyncEventData();

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

        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
                VsyncEventData vsyncEventData) {
            try {
                ···忽略部分代码···

                //注释2
                mTimestampNanos = timestampNanos;
                mFrame = frame;
                mLastVsyncEventData = vsyncEventData;
                Message msg = Message.obtain(mHandler, this);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }FrameDisplayEventReceiver

        @Override
        public void run() {
            //注释3
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
        }
    }

FrameDisplayEventReceiver 继承DisplayEventReceiver,实现Runnable接口。这里分别解释

DisplayEventReceiver

负责接收底层的VSync信号。VSync信号由SurfaceFliger实现,并定时发送。FrameDisplayEventReceiver收到信号后,会调用onVsync方法,在注释2处组织消息发送到对应的looper所在的线程处理。熟悉消息机制的应该明白这里处理的逻辑就是run方法(注释3)

扩展知识点:同步屏障和异步消息的处理

注释2处为什么msg.setAsynchronous(true)?

是为了优先处理run方法。当同步屏障触发后,会优先处理异步消息。详细可参考MessageQueue.next()

Message next() {
    ···忽略代码···
    			if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
    ···忽略代码···
}
CallbackQueue
  • 了解callback如何获取要回调的callback,
  • 如何添加callback到队列
  • 如何从队列移除callabck
  • 了解callback如何缓存

重点看三个方法

        //直译:提取dueCallback(看代码意思是,提取要触发的callback)
		public CallbackRecord extractDueCallbacksLocked(long now) {
            //取出队头
            CallbackRecord callbacks = mHead;
            if (callbacks == null || callbacks.dueTime > now) {
                //如果没有队头,没有要触发的callback,直接返回null
                //如果callbacl要执行的时间晚于当前时间,没有满足条件的callback,返回null
                return null;
            }
            //不满足之前的条件,说明有满足条件的callback,
            //重新确认队头
            //遍历队列,如果next callback要触发的时间,早于当前时间,则继续next遍历
            //反之,如果要触发的时间晚于当前时间,则打破队列,将next作为队头
            CallbackRecord last = callbacks;
            CallbackRecord next = last.next;
            while (next != null) {
                if (next.dueTime > now) {
                    last.next = null;
                    break;
                }
                last = next;
                next = next.next;
            }
            mHead = next;
            return callbacks;
        }

按照dueTime添加callback

		public void addCallbackLocked(long dueTime, Object action, Object token) {
            //从mCallbackPool中取出队头,mCallbackPool是CallbackRecord的缓存池
            CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
            //取出队头
            CallbackRecord entry = mHead;
            if (entry == null) {
                //如果对头null,则将这个callback作为队头
                mHead = callback;
                return;
            }
            //不为null,则判断时间是否早于队头,如果早于队友,则将这个callback放到队头
            if (dueTime < entry.dueTime) {
                callback.next = entry;
                mHead = callback;
                return;
            }
            //如果时间晚于队头,则按时间顺序插入到对应的队列中
            while (entry.next != null) {
                if (dueTime < entry.next.dueTime) {
                    callback.next = entry.next;
                    break;
                }
                entry = entry.next;
            }
            entry.next = callback;
        }

移除指定token或者action的callback

		public void removeCallbacksLocked(Object action, Object token) {
            CallbackRecord predecessor = null;
            for (CallbackRecord callback = mHead; callback != null;) {
                final CallbackRecord next = callback.next;
                //callback的action,或者token符合条件
                if ((action == null || callback.action == action)
                        && (token == null || callback.token == token)) {
                    //如果前任不为null
                    if (predecessor != null) {
                        //前任 = 前任的下一个,即当前的callback被移除出去
                        predecessor.next = next;
                    } else {
                        //header后移,因为这个callback要被移除出去
                        mHead = next;
                    }
                    //放入缓存池,这里放入的实队头,见注释4
                    recycleCallbackLocked(callback);
                } else {
                    predecessor = callback;
                }
                callback = next;
            }
        }

注释4,缓存callback

    private void recycleCallbackLocked(CallbackRecord callback) {
        callback.action = null;
        callback.token = null;
        //放到了mCallbackPool的队头
        callback.next = mCallbackPool;
        mCallbackPool = callback;
    }

View刷新的整个流程

为了方便看懂,能画图尽量不贴代码,直接上图
在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值