-
Notifications
You must be signed in to change notification settings - Fork 118
Choreographer详解
-
ViewRootImpl的构造方法中会调用Choreographer的getInstance,并将Choreographer做成成员变量进行保存。
-
Choreographer的getInstance则是从sThreadInstance中取出Choreographer
-
sThreadInstance是一个ThreadLocal类型的静态成员变量,ThreadLocal初始化值实例化了一个Choreographer。
-
Choreographer的构造方法中会实例化一个Handler,然后根据是否使用Vsync来创建FrameDisplayEventReceiver,接着创建一个容量为4的CallbackQueues数组,并且实例化四个Callback存入数组中。
-
CallbackQueues数组用于保存4中不同类型的Callback,分别为输入类型的callback、动画类型的callback、绘制类型的callback以及提交类型的callback
-
Vsync的注册、申请、接收都是通过FrameDisplayEventReceiver实现的,注册之后会监听SurfaceFlinger的传来的Vsync事件,在收到Vsync事件后回调onVsync
-
onVsync中通过Handler将FrameDisplayEventReceiver自身添加到Message中post了出去,
-
FrameDisplayEventReceiver实现了Runnable,因此执行Message时会执行它的run方法
-
在run方法中调用了doFrame开启帧绘制流程。
-
-
doFrame中主会进行掉帧逻辑计算、记录帧绘制的信息、然后执行doCallback。
- Choreographer.doFrame() 处理 Choreographer 的第一个 callback : input
- Choreographer.doFrame() 处理 Choreographer 的第二个 callback : animation
- Choreographer.doFrame() 处理 Choreographer 的第三个 callback : insets animation
- Choreographer.doFrame() 处理 Choreographer 的第四个 callback : traversal
-
开始执行View的绘制流程
在Android 4.1版本之前,由于屏幕的绘制没有一个良好的绘制周期和缓冲,丢帧和屏幕撕裂的问题非常严重。Android4.1版本之后引入了Vsync、TripleBuffer和Choreographer来优化显示性能。
Choreographer与Vsync配合,为上层APP的渲染提供一个稳定的Message处理时机。目前大部分手机都是60Hz的刷新率,即16.6ms刷新一次。系统为了配合屏幕的刷新频率,将Vsync的周期也设置为16.6ms。每隔16.6ms,Vsync信号就会唤醒Chroeographer来执行APP的绘制操作。
Choreographer 扮演 Android 渲染链路中承上启下的角色:
-
承上 负责接收和处理APP的各种更新消息和回调,等到Vsync到来时统一处理。比如集中处理Input事件、Animation、Traversal(measure、layout、draw),判断卡顿掉帧情况,记录Callback耗时等。
-
启下 负责请求和接收Vsync信号。
Choreographer + SurfaceFlinger + Vsync + TripleBuffer 这一套从上到下的机制,保证了 Android App 可以以一个稳定的帧率运行,减少帧率波动带来的不适感.
Choreographer的初始化是使用ThreadLocal,可以看到,在ThreadLocal的初始化值时候实例化了Choreographer
// Thread local storage for the choreographer.
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
// 获取当前线程Looper
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
// 实例化Choreographer
return new Choreographer(looper);
}
};
从上述代码可以看到,每个Looper都有自己的Choreographer,其他线程发送的回调只能运行在对应的Choreographer所属的Looper线程上。
可以通过Choreographer的getInstance方法从ThreadLocal中获取Choreographer
public static Choreographer getInstance() {
return sThreadInstance.get();
}
在ViewRootImpl的构造方法中就有此代码:
public ViewRootImpl(Context context, Display display) {
...
mChoreographer = Choreographer.getInstance();
}
Choreographer的构造方法如下:
private Choreographer(Looper looper) {
mLooper = looper;
// 初始化FrameHandler
mHandler = new FrameHandler(looper);
// 根据是否使用了VSYNC来创建一个FrameDisplayEventReceiver
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
// CALLBACK_LAST + 1 = 4,创建一个容量为4的CallbackQueue数组,用来存放4种不同的Callback
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
上述代码中先创建了一个Handler,接着使用USE_VSYNC查看是否使用了Vsync同步机制。如果是,则创建一个FrameDisplayEventReceiver对象用来请求并接受Vsync事件。
接着,创建了一个大小为4的数组,用于保存不同类型的Callback,有如下四种:
/**
*输入Callback类型,Runs first.
*/
public static final int CALLBACK_INPUT = 0;
/**
* 动画Callback类型,Runs before traversals.
*/
public static final int CALLBACK_ANIMATION = 1;
/**
* View绘制Callback类型,处理layout和draw. Runsafter all other
* asynchronous messages have been handled.
*/
public static final int CALLBACK_TRAVERSAL = 2;
/**
* 提交Callback类型. Handles post-draw operations for the frame.
* Runs after traversal completes.
*/
public static final int CALLBACK_COMMIT = 3;
Vsync的注册、申请、接收都是通过FrameDisplayEventReceiver实现的。 FrameDisplayEventReceiver 继承 DisplayEventReceiver , 有三个比较重要的方法,代码如下:
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper) {
super(looper);
}
// Vsync信号回调
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
// 请求Vsync信号
scheduleVsync();
return;
}
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;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
// scheduleVsync方法位于DisplayEventReceiver中,用来请求Vsync信号,如下
// public void scheduleVsync() {
// ...
// nativeScheduleVsync(mReceiverPtr);
// }
// 执行doFrame
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
FrameDisplayEventReceiver在初始化的时候通过监听文件句柄在Vsync信号到来时回调onVsync,如下:
// android/view/DisplayEventReceiver.java
public DisplayEventReceiver(Looper looper, int vsyncSource) {
......
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource);
}
简单来说,FrameDisplayEventReceiver 的初始化过程中,通过 BitTube(本质是一个 socket pair),来传递和请求 Vsync 事件,当 SurfaceFlinger 收到 Vsync 事件之后,通过 appEventThread 将这个事件通过 BitTube 传给 DisplayEventDispatcher ,DisplayEventDispatcher 通过 BitTube 的接收端监听到 Vsync 事件之后,回调 Choreographer.FrameDisplayEventReceiver.onVsync ,触发开始一帧的绘制.
Choreographer 处理绘制的逻辑核心在 Choreographer.doFrame 方法中,FrameDisplayEventReceiver.onVsync post 了自己,其 run 方法直接调用了 doFrame 开始一帧的逻辑处理。doFrame 函数主要做下面几件事:
- 计算掉帧逻辑
- 记录帧绘制信息
- 执行 CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_INSETS_ANIMATION、CALLBACK_TRAVERSAL、CALLBACK_COMMIT
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
...
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
...
}
...
}
Vsync 信号到来的时候会标记一个 start_time ,执行 doFrame 的时候标记一个 end_time ,这两个时间差就是 Vsync 处理时延,也就是掉帧。
Choreographer 中 FrameInfo 来负责记录帧的绘制信息,doFrame 执行的时候,会把每一个关键节点的绘制时间记录下来,我们使用 dumpsys gfxinfo 就可以看到。当然 Choreographer 只是记录了一部分,剩余的部分在 hwui 那边来记录。
doFrame 函数记录从 Vsync time 到 markPerformTraversalsStart 的时间
void doFrame(long frameTimeNanos, int frame) {
...
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
// 处理 CALLBACK_INPUT Callbacks
mFrameInfo.markInputHandlingStart();
// 处理 CALLBACK_ANIMATION Callbacks
mFrameInfo.markAnimationsStart();
// 处理 CALLBACK_INSETS_ANIMATION Callbacks
// 处理 CALLBACK_TRAVERSAL Callbacks
mFrameInfo.markPerformTraversalsStart();
// 处理 CALLBACK_COMMIT Callbacks
...
}
void doFrame(long frameTimeNanos, int frame) {
...
// 处理 CALLBACK_INPUT Callbacks
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
// 处理 CALLBACK_ANIMATION Callbacks
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
// 处理 CALLBACK_INSETS_ANIMATION Callbacks
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
// 处理 CALLBACK_TRAVERSAL Callbacks
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
// 处理 CALLBACK_COMMIT Callbacks
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
...
}
Input 回调调用栈一般是执行 ViewRootImpl.ConsumeBatchedInputRunnable
// android/view/ViewRootImpl.java
final class ConsumeBatchedInputRunnable implements Runnable {
@Override
public void run() {
doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
}
}
void doConsumeBatchedInput(long frameTimeNanos) {
if (mConsumeBatchedInputScheduled) {
mConsumeBatchedInputScheduled = false;
if (mInputEventReceiver != null) {
if (mInputEventReceiver.consumeBatchedInputEvents(frameTimeNanos)
&& frameTimeNanos != -1) {
scheduleConsumeBatchedInput();
}
}
doProcessInputEvents();
}
}
Input 时间经过处理,最终会传给 DecorView 的 dispatchTouchEvent,这就到了我们熟悉的 Input 事件分发.
Traversal 调用栈
// android/view/ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
执行ViewRootImpl的scheduleTraversals开启测量绘制流程。
- JMM与volatile关键字
- synchronized的实现原理
- synchronized等待与唤醒机制
- AQS的实现原理
- ReentrantLock的实现原理
- ReentrantLock等待与唤醒机制
- CAS、Unsafe类以及Automic并发包
- ThreadLocal的实现原理
- 线程池的实现原理
- Java线程中断机制
- 多线程与并发常见面试题
- Android基础知识汇总
- MVC、MVP与MVVM
- SparseArray实现原理
- ArrayMap的实现原理
- SharedPreferences
- Bitmap
- Activity的启动模式
- Fragment核心原理
- 组件化项目架构搭建
- 组件化WebView架构搭建
- 为什么 Activity.finish() 之后 10s 才 onDestroy ?
- Binder与AIDL
- Binder实现原理
- Android系统启动流程
- InputManagerService
- WindowManagerService
- Choreographer详解
- SurfaceFlinger
- ViewRootImpl
- ActivityManagerService
- APP启动流程
- 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. 有序数组的平方