Android屏幕刷新机制(二): invalidate()发生了什么
参考
文章完全参考自:Android 屏幕刷新机制,简化了部分代码逻辑,加了点自己的理解。
invalidate()
如果大家了解View体系里的相关知识应该知道,每个Activity 的根布局都是 DecorView
,DecorView
的 parent 又是 ViewRootImpl
,所以子view执行invalidate()方法时最终会引导到ViewRootImpl
中来。
看ViewRootImpl
的 invalidate()
方法:
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//看这句:postCallback
mChoreographer.postCallback(
// mTraversalRunnable
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
mChoreographer.postCallback()
第二个参数传入了一个runnable对象,先看这个对象是啥。
TraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
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
中来,这个方法是界面刷新的源头。这个方法使得ViewTree开始View的工作流程。performMeasure、performLayout、performDraw方法都在里面。
回过头再看这行代码中的这个方法:
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
Choreographer.postCallback
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {
...
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
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);
}
}
}
哦豁,期盼许久的addCallbackLocked
,在这里出现了。上一篇不是不知道传进来的token、action是什么么?现在知道了。token是null,action是TraversalRunnable
,所以屏幕发送VSync信号后,系统走的是TraversalRunnable.run()
方法。
然后到了performTraversals
中来,开始重绘界面。
而invalidate()
方法所做的,不过是向CallbackQueue
中添入一个CallbackRecord
。
scheduleFrameLocked
还没走完,下面还有几行。一个判断,看看scheduleFrameLocked
方法。
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
//这个mHandler,便是我们久违的FrameHandler
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);//2
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
// If running on the Looper thread, then schedule the vsync immediately,
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
//翻译:如果在主线程(Looper)就立即计划(schedule)一个vsync,否则快马加鞭传信给主线程,
//让主线程 马上(as soon as possible) 计划(schedule)一个vsync。
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);//1
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);//0
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
这里的mHandle
,便是我们久违的FrameHandler
。看样子是分不同的情况,传不同的信息(message)走FrameHandler
了。官方的注释也写的很清楚,尽快计划一个VSync,虽然不知道计划VSync是什么鬼。
那就看代码:追踪scheduleVsyncLocked();
会发现最终会走到DisplayEventReceiver.scheduleVsync()
方法中来。
/**
* Schedules a single vertical sync pulse to be delivered when the next
* display frame begins.
* 计划一个VSync脉冲,用于下一帧到来时的交付。
*/
@UnsupportedAppUsage
public void scheduleVsync() {
if (mReceiverPtr == 0) {
Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
+ "receiver has already been disposed.");
} else {
nativeScheduleVsync(mReceiverPtr);
}
}
可见最后在这里会注册一个监听器,表明已经有屏幕绘制的安排了,下一帧要用我绘制的帧。
再看看另外三种情况:
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);
break;
case MSG_DO_SCHEDULE_VSYNC:
//走scheduleVsyncLocked,还是立即执行了
doScheduleVsync();
break;
//做个判断,然后走scheduleFrameLocked,还是立即执行了
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
void doScheduleVsync() {
synchronized (mLock) {
if (mFrameScheduled) {
scheduleVsyncLocked();
}
}
}
void doScheduleCallback(int callbackType) {
synchronized (mLock) {
if (!mFrameScheduled) {
final long now = SystemClock.uptimeMillis();
if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
scheduleFrameLocked(now);
}
}
}
}
可见,除了MSG_DO_FRAME,其他的情况都是走向了scheduleVsync,注册了下一帧的监听。MSG_DO_FRAME的意思就是这一帧没有图像了,系统又doFrame循环一次去取下一帧的图像了。
总结
最后,放张流程图总结。
image.png
网友评论