-
Notifications
You must be signed in to change notification settings - Fork 139
Choreographer详解
在Android 4.1版本之前,由于屏幕的绘制没有一个良好的绘制周期和缓冲,丢帧和屏幕撕裂的问题非常严重。
Google 在 2012 年的 I/O 大会上宣布 Project Butter 计划,并在 Android 4.1 中正式开始实施,以优化 UI 渲染流畅性的问题。Project Butter 对 Android Display 系统进行了重构优化,引入了三个核心元素,即 VSYNC、Triple Buffering 和 Choreographer,本文重点深入学习 Choreographer 有关的知识。
- VSYNC 垂直同步
GPU 厂商开发用于防止屏幕撕裂的技术方案,全称 Vertical Synchronization,即垂直同步,可简单的把它理解为一种时钟中断。
- 刷新频率(Refresh Rate)
屏幕在一秒内刷新画面的次数,刷新频率取决于硬件的固定参数,单位:Hz(赫兹)。如常见的 60 Hz、144 Hz,即每秒钟刷新 60 次或 144 次。
- 逐行扫描
显示器并不是一次性将画面的像素显示到屏幕上,而是从左到右边,从上到下逐行扫描显示像素点,不过这一过程快到人眼无法察觉到变化。以 60 Hz 刷新率的屏幕为例,即 1000 / 60 ≈ 16ms。
- 帧速率 (Frame Rate)
表示 GPU 在一秒内绘制操作的帧数,单位:fps,全称 Frames Per Second,即每秒传输帧数。如在电影界采用 24 帧的速度足够使画面运行的非常流畅。而 Android 系统则采用更加流畅的 60 fps,即每秒钟绘制 60 帧画面。
Choreographer 直译为:舞蹈编导、编舞者,本质是一个 Java 类。简单来说,Choreographer 主要作用是协调输入、动画、和绘制等任务的执行时机,它从显示子系统接收定时脉冲(如垂直同步),然后安排渲染下一个显示 Frame 的部分工作。
还可以使用 Choreographer 来监测应用的帧率,结合自定义 FrameCallback 使用,在下一个显示 Frame 被渲染时将触发 FrameCallback 接口类获取渲染完成时间。
Choreographer 与 Vsync 配合,为上层 APP 的渲染提供一个稳定的 Message 处理时机。假定当前手机的是 60Hz 的刷新率,即 16.6ms 刷新一次。系统为了配合屏幕的刷新频率,将 Vsync 的周期也设置为 16.6ms。每隔16.6ms,Vsync信号就会唤醒 Chroeographer来执行APP的绘制操作。
public final class Choreographer {
// ThreadLocal 存储 Choreographer
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
// 获取当前线程的 Looper 对象
Looper looper = Looper.myLooper();
// 如果当前线程没有关联 Looper 则抛出异常提示
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
// 使用当前线程的 Looper 创建 Choreographer 实例,注意这里传的第二个参数 source 是 VSYNC_SOURCE_APP
// 这个值是 DisplayEventReceiver.VSYNC_SOURCE_APP 也就是 0
// 与 ISurfaceComposer.h 中的 VsyncSource.eVsyncSourceApp 值保持同步,代表的就是 AppEventThread
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
// 如果 Looper 是主线程的,则将 choreographer 赋值给 mMainInstance
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};
// Choreographer 静态实例
private static volatile Choreographer mMainInstance;
// 线程级单例,获取调用线程的 Choreographer 实例,必须由已关联有 Looper 的线程调用,否则抛出异常
public static Choreographer getInstance() {
return sThreadInstance.get();
}
}可以看到 Choreographer 内部有一个静态的 sThreadInstance,由于 sThreadInstance 是一个静态成员变量。因此 sThreadInstance 会在 Choreographer 的类加载时被初始化。源码中重写了 ThreadLocal 的 initialValue 方法。在这个方法中首先获取了当前线程的 Looper,然后实例化了Choreographer,并将其存入到了 ThreadLocal 中。
另外,Choreographer 还提供了一个 getInstance 方法。从 sThreadInstance 中来获取 Choreographer 。由此可见 Choreographer 与 Looper 一样都是一个线程级别的单例。
Choreographer 在其构造方法中会初始化一些用到的数据。其构造方法如下:
public final class Choreographer {
public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
private Choreographer(Looper looper, int vsyncSource) {
// 当前线程的 Looper
mLooper = looper;
// 创建 FrameHandler 处理消息
mHandler = new FrameHandler(looper);
// 是否启用 VSync,启用则创建 FrameDisplayEventReceiver 对象接收 VSync 脉冲信号
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
// 初始化上一个 frame 渲染的时间点
mLastFrameTimeNanos = Long.MIN_VALUE;
// getRefreshRate() 获取刷新率,Android 手机屏幕是 60Hz 的刷新频率
// 计算帧率,即渲染一帧的时间约等于 16666.66ns ≈ 16.6ms
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
// 创建回调队列 CallbackQueue 默认容量大小是 5 的链表数组,存放要执行的输入、动画、绘制等任务
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
// 每个子元素为链表,用于存相同类型的任务:输入、动画、绘制等任务
//(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
}Choreographer 的构造方法的核心有以下几点:
- 首先实例化了一个 FrameHandler,FrameHandler 是一个继承自 Handler 的类。这个后边再分析。
- 根据 USE_VSYNC 变量判断当前是否启用 Vsync 同步机制。该值是通过读取系统属性 debug.choreographer.vsync 来获取的,Android 在 4.1 之后默认启用该机制。因此这里默认创建了 FrameDisplayEventReceiver 对象用户请求并接受 Vsync 信号。
- 给 mLastFrameTimeNanos 赋初始值,mLastFrameTimeNanos 表示上一帧渲染的开始时间。
- 根据刷新了计算每帧画面的时间间隔并保存到 mFrameIntervalNanos
- 初始化一个 mCallbackQueues 的数组,这个数组长度为5,表示 5 种不同的任务类型,例如输入(CALLBACK_INPUT)、动画(CALLBACK_ANIMATION)、**绘制(CALLBACK_TRAVERSAL)**等类型。数组的成员类型为 CallbackQueue。CallbackQueue 内部封装了一个 CallbackRecord 类型的链表,每种类型任务可能有多个,相同的类型以链表形式存储。
构造方法中初始化了一些成员变量,其中比较重要的两个成员变量是 FrameHandler 与 FrameDisplayEventReceiver
FrameHandler 是 Choreographer 的一个内部类,源码如下:
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;
}
}
}- MSG_DO_FRAME 当收到 Vsync 信号后执行 doFrame 开始渲染页面帧。
- MSG_DO_SCHEDULE_VSYNC 向系统底层请求下一次VSYNC信号
- MSG_DO_SCHEDULE_CALLBACK 请求执行延迟任务回调*
FrameDisplayEventReceiver 也是 Choreographer 中的一个内部类。 源码如下:
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
private final VsyncEventData mLastVsyncEventData = new VsyncEventData();
FrameDisplayEventReceiver(Looper looper, int vsyncSource, long layerHandle) {
super(looper, vsyncSource, /* eventRegistration */ 0, layerHandle);
}
// TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for
// the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC
// for the internal display implicitly.
@Override
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);
}
// Post the vsync event to the Handler.
// The idea is to prevent incoming vsync events from completely starving
// the message queue. If there are no messages in the queue with timestamps
// earlier than the frame time, then the vsync event will be processed immediately.
// Otherwise, messages that predate the vsync event will be handled first.
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.copyFrom(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);
}
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
}
}可以看到 FrameDisplayEventReceiver 继承了 DisplayEventReceiver。DisplayEventReceiver 是一个抽象类。当 Vsync 信号到来时会调用 DisplayEventReceiver 的 dispatchVsync 方法,在这个方法中又调用了 onVsync。而FrameDisplayEventReceiver 重写了 onVsync 方法。
onVsync 方法的核心是构建了一个 Message ,这个 Message 持有 FrameDisplayEventReceiver,FrameDisplayEventReceiver 实现了 Runnable 接口。 因此在通过 FrameHandler 发送消息后最终会执行 FrameDisplayEventReceiver 的 run 方法。
而 run 方法中的核心则是调用了 doFrame 去完成帧的绘制逻辑。doFrame 方法的源码如下:
public final class Choreographer {
// Set a limit to warn about skipped frames.
// Skipped frames imply jank.
private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt(
"debug.choreographer.skipwarning", 30);
// ...
void doFrame(long frameTimeNanos, int frame,
DisplayEventReceiver.VsyncEventData vsyncEventData) {
final long startNanos;
final long frameIntervalNanos = vsyncEventData.frameInterval; // 帧间隔
try {
// ...
synchronized (mLock) {
// ...
// 设置将要处理的当前帧的时间戳
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime(); // 记录实际开始执行当前 frame 的时间
final long jitterNanos = startNanos - frameTimeNanos; // 计算时间差值
if (jitterNanos >= frameIntervalNanos) { // 时间差值大于等于帧间隔,即发生了跳帧
final long skippedFrames = jitterNanos / frameIntervalNanos; // 计算跳帧数
// 跳帧超过 30 打印日志提醒,SKIPPED_FRAME_WARNING_LIMIT 通过读取
// 系统属性 debug.choreographer.skipwarning 来获取的,默认是 30
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
// 计算实际开始当前 frame 的时间戳与帧间隔的偏移值
final long lastFrameOffset = jitterNanos % frameIntervalNanos;
// ...
// 修正偏移值,下一帧的时间戳开始值要减去计算得出的偏移值
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
// ...
traceMessage("Frame time goes backward");
// 当前帧的时间小于上一个帧的时间,可能是由于先前跳帧的缘故,申请并等待下一次 VSync 信号到来
scheduleVsyncLocked();
return;
}
if (mFPSDivisor > 1) {
long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
traceMessage("Frame skipped due to FPSDivisor");
// 由于 FPSDivisor 导致跳帧,继续申请并等待下一次 VSync 信号到来
scheduleVsyncLocked();
return;
}
}
// 记录当前 Frame 信息
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos, vsyncEventData.id,
vsyncEventData.frameDeadline, startNanos, vsyncEventData.frameInterval);
// 收到 VSync 信号当前帧调度完,mFrameScheduled 标志位重置为 false,以便下一轮请求使用
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos; // 记录上一次 Frame 渲染的时间
mLastFrameIntervalNanos = frameIntervalNanos; // 记录上一次 Frame 渲染的帧间隔
mLastVsyncEventData = vsyncEventData; // 记录上一次 VSync 事件信息
}
// 动画锁定为当前线程的固定值
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
// 按照事件类型的优先级执行 CallBack
mFrameInfo.markInputHandlingStart();
// 输入事件,首先执行
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, frameIntervalNanos);
mFrameInfo.markAnimationsStart();
// 动画事件,在 CALLBACK_INSETS_ANIMATION 之前执行
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, frameIntervalNanos);
// 插入更新动画事件,INPUT 和 ANIMATION 后面执行,TRAVERSAL 之前执行
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos, frameIntervalNanos);
// 处理布局和绘制事件,在处理完上述异步消息之后运行
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, frameIntervalNanos);
// 提交,和提交任务有关(在 API Level 23 添加),最后执⾏,遍历完成的提交操作,⽤来修正动画启动时间
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, frameIntervalNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
// ...
}
// ...
}- 获取正在运行的 Java 虚拟机的当前时间,单位为纳秒,然后从数组链表 mCallbackQueues 中获取指定类型的链表 CallbackQueue,之后调用 CallbackQueue # extractDueCallbacksLocked() 方法并传入刚才获取到的时间,来确定链表 CallbackQueue 中是否有 CallbackRecord 的执行时间已经到了。参见 3.4 回调队列 CallBackQueue 中的图示和介绍。
- 遍历获取到的 callbacks 链表获取 CallbackRecord 对象,然后执行 CallbackRecord 对象的 run() 方法。
void doCallbacks(int callbackType, long frameTimeNanos, long frameIntervalNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
// 获取正在运行的 Java 虚拟机的当前时间,单位为纳秒,以此来确定回调何时执行
// 因为帧中的前期处理阶段可能会发布应在下一阶段运行的回调,例如导致动画启动的输入事件
final long now = System.nanoTime();
// 从数组链表 mCallbackQueues 中获取指定类型的链表
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) { // 没有查找到 callbackType 对应的链表,则直接返回
return;
}
mCallbacksRunning = true;
// 对于 CALLBACK_COMMIT 类型的事件要更新调整其执行时间、不过多讨论
if (callbackType == Choreographer.CALLBACK_COMMIT) {
// ...
}
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
// 遍历 callbacks 链表获取 CallbackRecord 对象
for (CallbackRecord c = callbacks; c != null; c = c.next) {
......
// 执行 CallbackRecord 对象的 run() 方法
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
// 回收 callbacks,置空其内部 CallbackRecord 保存的信息,重组并赋值给 mCallbackPool 链表
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
// ...CallbackRecord 的 run() 方法中,根据 token 判断当前回调类型,这里暂时不分析 FRAME_CALLBACK_TOKEN 类型的,只分析 Runnable 并执行其 run() 方法,在 1. 初探 Choreographer 的使用 中通过 Choreographer # postCallback() 方法提交一个任务 TraversalRunnable,将任务 TraversalRunnable 和执行时间一起被封装成 CallbackRecord,因此这里执行的就是 TraversalRunnable 的 run() 方法。
上一节中分析了 Choreographer 的初始化过程。其中提到 mCallbackQueues 数组中存储了5 种不同类型的任务类型。下面举两个类型的 CallbackQueue 在系统源码中使用的例子来更全面的了解 Choreographer 。
在分析学习 View 绘制流程有关的源码时,刷新 View 可以使用 View # invalidate() 方法 或 View # requestLayout() 方法,但不管调用哪一个方法,流程都会走到 ViewRootImpl # scheduleTraversals() 方法,代码如下:
// ViewRootImpl 初始化时会实例化 Choreographer
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
final Choreographer mChoreographer;
public ViewRootImpl(Context context, Display display, IWindowSession session,
boolean useSfChoreographer) {
......
mChoreographer = useSfChoreographer
? Choreographer.getSfInstance() : Choreographer.getInstance();
......
}
@UnsupportedAppUsage
void scheduleTraversals() {
// 注意这个标志位,多次调用时只有这个标志位为 false 时才有效,保证同时有多次调用时只会执行一次
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 通过 MessageQueue # postSyncBarrier() 设置 Handler 的同步屏障消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// Choreographer 通过 postCallback 提交一个任务,mTraversalRunnable 是要执行的回调
// 注意这里 token 传值为 null
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
}在 scheduleTraversals 中会通过 Choreographer 的 postCallback 提交一个 CALLBACK_TRAVERSAL 的Runnable。postCallback 源码如下:
@UnsupportedAppUsage
@TestApi
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
/**
* Posts a callback to run on the next frame after the specified delay.
* <p>
* The callback runs once then is automatically removed.
* </p>
*
* @param callbackType The callback type.
* @param action The callback action to run during the next frame after the specified delay.
* @param token The callback token, or null if none.
* @param delayMillis The delay time in milliseconds.
*
* @see #removeCallback
* @hide
*/
@UnsupportedAppUsage
@TestApi
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
if (action == null) {
throw new IllegalArgumentException("action must not be null");
}
if (callbackType < 0 || callbackType > CALLBACK_LAST) {
throw new IllegalArgumentException("callbackType is invalid");
}
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}最终,在 postCallbackDelayedInternal 方法中,获取了 CALLBACK_TRAVERSAL 对应的 CallbackQueue,并调用了其 addCallbackLocked 将 Runnable 作为参数传入。addCallbackLocked 源码如下:
public void addCallbackLocked(long dueTime, Object action, Object token) {
CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
CallbackRecord entry = mHead;
if (entry == null) {
mHead = callback;
return;
}
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;
}可以看到上述代码 action 封装到了 CallbackRecord 中,并根据 dueTime 将其插入到了链表的合适位置。
ValueAnimator 是 Android 动效中经常用到的一个类,它的使用方法如下:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,1);
valueAnimator.setDuration(200);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
Log.e("ValueAnimator","animatedValue-------"+animatedValue);
}
});
valueAnimator.start();ValueAnimator 的实现其实也和 Choreographer 有关系。看下 ValueAnimator#start 的源码:
private void start(boolean playBackwards) {
// ...
addAnimationCallback(0);
// ...
}start 方法中调用了 addAnimationCallback 方法。addAnimationCallback 源码如下:
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}又调用了 AnimationHandler 的 addAnimationFrameCallback 方法,addAnimationFrameCallback 方法源码如下:
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
// ...
}getProvider 方法获取到的是一个 MyFrameCallbackProvider 的实例,它的 postFrameCallback 源码如下:
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}可见这里是调用了 Choreographer 的 postFrameCallback 方法。继续看下 postFrameCallback 的源码:
public void postFrameCallback(FrameCallback callback) {
postFrameCallbackDelayed(callback, 0);
}
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}可以看到,这里最终跟 View 的渲染流程一样调用了 postCallbackDelayedInternal 方法,不同的是这里的类型是 CALLBACK_ANIMATION。
参考: https://blog.csdn.net/u010347226/article/details/125172662
- JMM与volatile关键字
- synchronized的实现原理
- synchronized等待与唤醒机制
- ReentrantLock的实现原理
- ReentrantLock等待与唤醒机制
- CAS、Unsafe类以及Automic并发包
- ThreadLocal的实现原理
- 线程池的实现原理
- Java线程中断机制
- 多线程与并发常见面试题
- Android基础知识汇总
- MVC、MVP与MVVM
- SparseArray实现原理
- ArrayMap的实现原理
- SharedPreferences
- Bitmap
- Activity的启动模式
- Fragment核心原理
- 组件化项目架构搭建
- 组件化WebView架构搭建
- 为什么 Activity.finish() 之后 10s 才 onDestroy ?
- Android系统启动流程
- InputManagerService
- WindowManagerService
- Choreographer详解
- SurfaceFlinger
- ViewRootImpl
- APP启动流程
- Activity启动流程
- PMS 安装与签名校验
- Dalvik 与 ART
- 内存优化策略
- UI界面及卡顿优化
- App启动优化
- ANR问题
- 包体积优化
- APK打包流程
- 电池电量优化
- Android屏幕适配
- 线上性能监控1--线上监控切入点
- 线上性能监控2--Matrix实现原理
- Glide实现原理
- OkHttp实现原理
- Retrofit实现原理
- RxJava实现原理
- RxJava中的线程切换与线程池
- LeakCanary实现原理
- ButterKnife实现原理
- ARouter实现原理
- Tinker实现原理
- 2. 两数相加
- 19.删除链表的倒数第 N 个结点
- 21. 合并两个有序链表
- 24. 两两交换链表中的节点
- 61. 旋转链表
- 86. 分隔链表
- 92. 反转链表 II
- 141. 环形链表
- 160. 相交链表
- 206. 反转链表
- 206 反转链表 扩展
- 234. 回文链表
- 237. 删除链表中的节点
- 445. 两数相加 II
- 面试题 02.02. 返回倒数第 k 个节点
- 面试题 02.08. 环路检测
- 剑指 Offer 06. 从尾到头打印链表
- 剑指 Offer 18. 删除链表的节点
- 剑指 Offer 22. 链表中倒数第k个节点
- 剑指 Offer 35. 复杂链表的复制
- 1. 两数之和
- 11. 盛最多水的容器
- 53. 最大子序和
- 75. 颜色分类
- 124.验证回文串
- 167. 两数之和 II - 输入有序数组 -169. 多数元素
- 189.旋转数组
- 209. 长度最小的子数组
- 283.移动0
- 303.区域和检索 - 数组不可变
- 338. 比特位计数
- 448. 找到所有数组中消失的数字
- 643.有序数组的平方
- 977. 有序数组的平方


