写点什么

Android- 怎么就不卡了呢之 Choreographer,怒斩腾讯和阿里的 Offer

用户头像
Android架构
关注
发布于: 2021 年 11 月 05 日

}


以上代码我们可以看出这个,FramHandler 拿到 whate 属性值为 MSG_DO_SCHEDULE_CALLBACK 的时候会去执行 doScheduleCallback(msg.arg1);方法,跟进去看下

1.3 Choreography#doScheduleCallback

void doScheduleCallback(int callbackType) {synchronized (mLock) {if (!mFrameScheduled) {final long now = SystemClock.uptimeMillis();if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {scheduleFrameLocked(now);}}}}


这个方法中先是做了一些判断,mFrameSceduled 为 false 并且 hasDueCallbacksLocked()这个方法的返回值为 true,看方法名就能猜出这个 callback 是否到期了,下面我们再分析这个。最终如果满足条件的情况下它会调用 scheduleFrameLocked()这个方法,咦这个方法眼熟不?对,没错,postCallbackDelayedInternal()方法中如果到期了的话就直接执行的那个方法。是时候看这个方法里面搞的什么事情了。

1.4 scheduleFrameLocked()

private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {mFrameScheduled = true;//设置标记位,表示已经安排请求下一帧渲染了。if (USE_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();} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);msg.setAsynchronous(true);mHandler.sendMessageAtFrontOfQueue(msg);}} else {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);}}}


  • 这个方法的目的很明确就是安排,安排垂直同步而且立刻马上尽快。安排垂直同步的条件是 USE_VSYNC 为 true,也就是设备支持垂直同步

  • 如果不是垂直同步就通过 handler 发送一个延时一个周期的消息安排垂直同步,这个 Message 的 what 值为 MSG_DO_FRAME,参照 1.2 的代码块对 what 为 MSG_DO_FRAME 的消息会去执行 doFrame()方法。

  • 一个细节,当这个值 mFrameScheduled 为 true 的时候就直接返回不安排请求下一帧渲染了,如果为 false,执行 scheduleFrameLocked()方法继续执行,并且将其设置为 ture;在什么时候设置为 false 的呢?详细细节看附录二


安排垂直同步的具体实现是 FrameDisplayEventReceiver 类他是 DisplayEventReceiver 的用于接收垂直信号


private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable {private boolean mHavePendingVsync;private long mTimestampNanos;private int mFrame;


public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {super(looper, vsyncSource);}@Overridepublic void onVsync(long timestampNanos, int builtInDisplayId, int frame) {mTimestampNanos = timestampNanos;mFrame = frame;Message msg = Message.obtain(mHandler, this);msg.setAsynchronous(true);//Message 设置为异步 mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);}@Overridepublic void run() {mHavePendingVsync = false;doFrame(mTimestampNanos, mFrame);}}


接收到垂直同步信号后回调 onVsync 方法,这个方法使用 handler 发送带 callback(Runnable 类型,自身已继承)的 message,最后 run()中也是调用 doFrame();(关于这个 handler 的这个操作详细信息逻辑,参照下面本文附录一 handler 分发message


这个 message 设置为了异步 (msg.setAsynchronous(true);)这意味这他有优先执行的权利,他是怎么被优先执行的呢?参照附录三 message的异步模式


综上,添加 callback 流程


二、执行

doFrame

void doFrame(long frameTimeNanos, int frame) {final long startNanos;synchronized (mLock) {if (!mFrameScheduled) {return; // no work to do}


//当前时间 startNanos = System.nanoTime();//当前时间和垂直同步时间 final long jitterNanos = startNanos - frameTimeNanos;//垂直同步时间和当前时间的差值如果大于一个周期就修正一下 if (jitterNanos >= mFrameIntervalNanos) {//取插值和始终周期的余数 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;//当前时间减去上一步得到的余数当作最新的始终信号时间 frameTimeNanos = startNanos - lastFrameOffset;}//垂直同步时间上一次时间还小,就安排下次垂直,直接返回 if (frameTimeNanos < mLastFrameTimeNanos) {scheduleVsyncLocked();return;}mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);mFrameScheduled = false;mLastFrameTimeNanos = frameTimeNanos;}


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);


mFrameInfo.markPerformTraversalsStart();doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);


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.");}}


  1. 第一步修正判断


  • 当前时间 startNanos = System.nanoTime();

  • 求当前时间和垂直同步时间的差值 :jitterNanos = startNanos - frameTimeNanos;

  • 垂直同步时间和当前时间的差值如果大于一个周期(jitterNanos >= mFrameIntervalNanos)就修正一下

  • 取插值和始终周期的余数:lastFrameOffset = jitterNanos % mFrameIntervalNanos;

  • 当前时间减去上一步得到的余数当作最新的始终信号时间:frameTimeNanos = startNanos - lastFrameOffset;

  • 垂直同步时间上一次时间还小,就安排下次渲染: frameTimeNanos < mLastFrameTimeNanos,直接返回


  1. 第二步?执行 callback callback 的执行顺序是:


  • CALLBACK_INPUT 输入时间优先级最高

  • CALLBACK_ANIMATION 动画的次之

  • CALLBACK_TRAVERSAL UI 绘制布局的再次之

  • CALLBACK_


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


COMMIT 动画修正相关最后。

2.2 doCallbacks();

  • CallbackQueue[] mCallbackQueues 在取特定类型(输入,动画,布局,Commit)的单向链表

  • 然后取出已到期的 Callback/Runable 执行

取出需要被执行的 Actions

Action 包装在 CallbackRecord 中,是一个单向列表,按照时间的大小顺序排列的。 取出待执行的 Actions 是通过 CallBackQueue 的 extractDueCallbacksLocked()方法,可以把 CallBackQueue 看做是 CallBack 的管理类,其中还包括添加 Action addCallbackLocked(),移除 Action removeCallbacksLocked(),是否有带起的 Anction hasDueCallbacksLocked()方法。


private final class CallbackQueue {//链表头 private CallbackRecord mHead;//是否存在已经到期的 Actionpublic boolean hasDueCallbacksLocked(long now) {return mHead != null && mHead.dueTime <= now;}//获取已经到期的 Actionpublic CallbackRecord extractDueCallbacksLocked(long now) {...return callbacks;}


//添加 Actionpublic void addCallbackLocked(long dueTime, Object action, Object token) {...}//移除 Actionpublic void removeCallbacksLocked(Object action, Object token) {...}}

执行 Action

for (CallbackRecord c = callbacks; c != null; c = c.next) {c.run(frameTimeNanos);}


从 callback 中遍历出 CallBcakRecord,挨个执行。

三、小结

  • Choreographer 对外提供了 postCallback 等方法,最终他们内部都是通过调用 postCallbackDelayedInternal()实现这个方法主要会做两件事情

  • 存储 Action

  • 请求垂直同步,垂直同步

  • 垂直同步回调立马执行 Action(CallBack/Runnable)。

  • Action 一个动作内容的类型可能是

  • CALLBACK_INPUT 输入时间优先级最高

  • CALLBACK_ANIMATION 动画的次之

  • CALLBACK_TRAVERSAL UI 绘制布局的再次之

  • CALLBACK_COMMIT 动画修正相关最后。

  • 复习 Hanlder 机制,我认为他是 Android 系统跑起来的大引擎终点关注下,handler 对 message 的分发执行,以及“异步模式”。

附一、关于 handler 执行 Message

下面是 handler 分发逻辑,Looper 在 MessageQueue 得到要执行的 message 之后就会交给 message 的 target(Handler 类型)属性处理 msg.target.dispatchMessage(msg);;


public void dispatchMessage(Message msg) {//当 msg 的 callback 不为空的时候直接执行 msg 的 callback 它是一个 Runnable 对象 if (msg.callback != null) {handleCallback(msg);} else {//然后再交给 mCallBack,它是 handler 的一个属性,//创建 Handler 的时候可以选择传入一个 CallBack 对象//当 callBack 中 handleMessage 返回 true 的时候表示:True if no further handling is desired(不需要进一步处理)


if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}//当 mCallback 处理返回为 false 的时候才去执行 Handler 自身的 handleMessage()方法 handleMessage(msg);}}


关键逻辑在已注释,小结一下 handler 的执行分发 Message 逻辑


  1. 如果 message 的 callback(runnable)属性不为空,调用这个 runable 的 run()方法执行


private static void handleCallback(Message message) {message.callback.run();

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android-怎么就不卡了呢之Choreographer,怒斩腾讯和阿里的Offer