接收屏幕的垂直同步信号,通过Handler发送Message,垂直同步信号和我们常说的刷新率相关,一般的手机刷新率是60,意思是每秒发送60个垂直同步信号:
#FrameDisplayEventReceiver
@Override
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
然后在Handler中处理Message,Message中what默认是0,因此发送Message的时候没有给what赋值:
private final FrameHandler mHandler;
private static final int MSG_DO_FRAME = 0;
private final class FrameHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
}
}
}
依次调用各种事件类型,调用的顺序表示了各自的优先级:
#Choreographer
public static final int CALLBACK_INPUT = 0;
public static final int CALLBACK_ANIMATION = 1;
public static final int CALLBACK_INSETS_ANIMATION = 2;
public static final int CALLBACK_TRAVERSAL = 3;
public static final int CALLBACK_COMMIT = 4;
void doFrame(long frameTimeNanos, int frame) {
try {
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
}
}
mCallbackQueues就是一个数组,数组中的每一个CallbackQueue,是一个事件的集合。
例如:CALLBACK_TRAVERSAL = 3
,那么mCallbackQueues的第四个集合存储的就是View刷新的信号。
集合中的项是CallbackRecord
实例,doCallbacks
方法会遍历集合,从中取出符合计时条件的项,然后执行run
方法:
#Choreographer
private final CallbackQueue[] mCallbackQueues;
void doCallbacks(int callbackType, long frameTimeNanos) {
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
try {
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
}
}
mCallbackQueues
是在Choreographer
构造方法中初始化的,初始化时就固定了size = 5
:
public static final int CALLBACK_COMMIT = 4;
private static final int CALLBACK_LAST = CALLBACK_COMMIT;
private Choreographer(Looper looper, int vsyncSource) {
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
到现在我们了解到,应用接收到垂直同步信号之后,会遍历队列中的项。那么,这些项是什么时候加入到队列中的呢?
以CALLBACK_TRAVERSAL
为例:
ViewRootImpl
中多处调用了scheduleTraversals
,其中有我们非常熟悉的requestLayout
和invalidate
等等
在scheduleTraversals
方法中,插入了CALLBACK_TRAVERSAL
到队列中:
#ViewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
synchronized (mLock) {
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
}
}
到这里,我们知道了队列中的项是如何添加进去的。
那么CALLBACK_TRAVERSAL
回调被执行后,做了哪些动作呢?
和CALLBACK_TRAVERSAL
一起添加到队列中的,还有一个实例mTraversalRunnable
:
#ViewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
mTraversalRunnable
的创建和定义:
#ViewRootImpl
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
doTraversal
调用了performMeasure、 performLayout、performDraw、
等方法。
梳理一下整体流程:
ViewRootImpl
接收到更新界面请求ViewRootImpl
添加回调到Choreographer
队列中Choreographer
接收到屏幕发送的同步信号,从队列中取出回调执行ViewRootImpl
的方法,调用View的相关方法,更新界面毫无疑问,上述流程是在主线程执行的。
应用每16ms收到一次同步信号,意味着主线程的代码必须在16ms以内执行完成。
超过16ms会发生丢帧。丢帧严重时,用户会感知到明显的卡顿。
使用Profiler能够方便定位到丢帧位置:
Android性能优化:使用Android Studio的Profiler分析函数执行时间
View是谁?从哪里来的?
ViewRootImpl、WindowManagerGlobal和WindowManager之间的关系