Android- 怎么就不卡了呢之 Choreographer,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.");}}
第一步修正判断
当前时间 startNanos = System.nanoTime();
求当前时间和垂直同步时间的差值 :jitterNanos = startNanos - frameTimeNanos;
垂直同步时间和当前时间的差值如果大于一个周期(jitterNanos >= mFrameIntervalNanos)就修正一下
取插值和始终周期的余数:lastFrameOffset = jitterNanos % mFrameIntervalNanos;
当前时间减去上一步得到的余数当作最新的始终信号时间:frameTimeNanos = startNanos - lastFrameOffset;
垂直同步时间上一次时间还小,就安排下次渲染: frameTimeNanos < mLastFrameTimeNanos,直接返回
第二步?执行 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 逻辑
如果 message 的 callback(runnable)属性不为空,调用这个 runable 的 run()方法执行
private static void handleCallback(Message message) {message.callback.run();}
当我们使用 handler.post(Runnable r)方法时候就是将 r 设置给 message 的 callback
上述条件不满足的情况下,如果 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
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 (;;) {
评论