写点什么

Android- 怎么就不卡了呢之 Choreographer,android 开发实例大全

用户头像
Android架构
关注
发布于: 刚刚

}


  • 这个方法的目的很明确就是安排,安排垂直同步而且立刻马上尽快。安排垂直同步的条件是 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_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();}


当我们使用 handler.post(Runnable r)方法时候就是将 r 设置给 message 的 callback


  1. 上述条件不满足的情况下,如果 handler 自身的 mCallback 不为空的时候就会,将 message 交给 mCallback 处理,handlerMessage()结束。这个属性是在 handler 创建的时候传入的。mCallback 是 CallBack 类型,他是 handler 的一个内部接口。


public interface Callback {boolean handleMessage(Message msg);}


3.当 messaga 的 callBak 为空,且 handler 的 mCallBack 为空的时候就交给自己的 handlerMessage()方法执行了。我们在自定义 handler 的时候可以重写这个方法对 message 进行相应的操作。

附二 、mFrameScheduled 属性作用

  • 执行 callcack 的时候会判断 mFrameScheduled 属性如果为 false 表示没有安排渲染下一帧就直接返回,不执行。


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


  • 在 scheduleFrameLocked()方法中,将 m


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


FrameScheduled 值设置为 ture 表示安排了请求渲染下一帧。如果这时 mFrameScheduled 为 true 表示已经安排了下一帧那么就返回,不添乱!

附三、Handler 机制的异步模式

作用

“异步模式”的作用就是优先,asynchronous 的 message 在异步模式下有优先执行的权。

用法

MessageQueue 使用 postSyncBarrier()方法添加屏障,removeSyncBarrier()方法移除屏障这个两个方法是成对使用的。

实现原理

  • messageQueued 的 postSyncBarrier 方法向 messagequeue 的头部添加一个 target 属性为 null 的 message

  • messageQueue 的 next()当碰到 target 为 null 的 message 的时候就只会在 message 链表中取出去“异步 message”,而忽略普通的 message,交给 Looper 做进一步分发处理。


Message next() {...for (;;) {

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android-怎么就不卡了呢之Choreographer,android开发实例大全