看过电影都知道,连续的场景其实是一帧一帧的静态图像,手机显示也是这样,只不过大部分手机刷新率增加到了每秒60帧,手机显示图像是逐行刷新的,如果一页的图像没有刷新完成,这个时候又有新的图像开始刷新就会产生画面撕裂,android在4.1版本的时候引入了一个黄油计划,用来解决系统UI响应的问题,通过强制垂直同步VSYNC信号来使图像产生和显示达到步调一致。
VSYNC信号是由底层驱动发出来的,上层应用接受这个信号的地方就是Choreographer类中的内部类FrameDisplayEventReceiver。
可以自己写一个自定义View,然后在onDraw方法中设置断点,查看方法调用栈就会发现这其实是一个带有callback的消息,以下是方法调用栈的截图:
1.png
这个callback如下图:
2.png
也可以在模拟器中直接打断点到SDK的代码,更方便。
然后在Handler源码中处理带有callback消息的代码是这样的:
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
handleCallback方法的实现如下:
private static void handleCallback(Message message) {
message.callback.run();
}
而FrameDisplayEventReceiver类继承了Runnable接口,看下具体的实现
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
...
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
也就是直接调用了run()方法中的doFrame()方法。看下doFrame()方法的实现
void doFrame(long frameTimeNanos, int frame) {
...
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);
}
...
}
执行了doCallbacks()方法,UI刷新执行的是CALLBACK_TRAVERSAL类型的,下面贴上doCallbacks()方法代码
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
...
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
执行callbacks的run方法,而callbacks来自CallbackQueue数组,CallbackQueue本身是一个内部维护了CallbackRecord的链表,继续看这个链表的添加方法
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long 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);
}
}
}
最终调用这个方法,讲callback传进来的方法是
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
调用Choreographer类这个方法的地方是在ViewRootImpl类里面:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
mTraversalRunnable就是刷新的callback
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
doTraversal()的方法
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
这里面performTraversals()方法就是开启view界面布局的核心代码,里面执行了performMeasure(),performLayout()等view布局流程的代码。
接着回到最前面,这个带有callback的消息是怎么产生的,这部分内容牵扯到了内核层的代码,而这块我不熟悉,因此参考了git袁的博客。
先来看类Choreographer的内部类FrameDisplayEventReceiver的继承关系:
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable
继承了DisplayEventReceiver类,来看下DisplayEventReceiver类的构造函数
/**
* Creates a display event receiver.
*
* @param looper The looper to use when invoking callbacks.
* @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values.
* @param configChanged Whether to dispatch config changed events. Must be one of the
* CONFIG_CHANGED_EVENT_* values.
*/
public DisplayEventReceiver(Looper looper, int vsyncSource, int configChanged) {
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
vsyncSource, configChanged);
mCloseGuard.open("dispose");
}
在构造函数中调用了JNI方法nativeInit,将Java层的消息队列传进去了,此处属于内核部分的原理,能力有限就不做深究,内核层中产生的vysnc信号,最终会调用到Java层的DisplayEventReceiver类的dispatchVsync方法:
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage
private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
onVsync(timestampNanos, physicalDisplayId, frame);
}
而onVsync方法在子类FrameDisplayEventReceiver中的实现就是创建了一个带有callback的msg:
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
...
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
mHandler的初始化在Choreographer的构造方法中:
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
mLastFrameTimeNanos = Long.MIN_VALUE;
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
// b/68769804: For low FPS experiments.
setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
}
这个looper就是主线程的looper,后续会来一章Choreographer创建的流程追踪。至此刷新UI的入口流程就完成了。
网友评论