- Android刷新机制
- SurfaceView理解
一、Android屏幕刷新机制
首先需要了解一些基本概念
在之前的几篇文章里,例如 Window和WindowManager相关知识点(六) 以及 Android View相关知识点以及原理(四) 内对DecorView和setContentView以及Window的联系可以知道在onResume的时候才会创建ViewRootImpl将DecorView和Window关联起来,并且和任意View调用invalidate等刷新一样都会走ViewRootImpl中的 scheduleTraversals()方法,然后调用Choreographer的postCallBack方法
void scheduleTraversals() {
if (!mTraversalScheduled) {
//编号1
mTraversalScheduled = true;
//编号2
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//编号3
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
Choreographer的postCallBack中传了一个mTraversalRunnable,大家可以看下面代码,run方法内调用了doTraversal方法
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
//编号4
mTraversalScheduled = false;
//编号5
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//这里开始对view树进行测量、布局、绘制
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
这里会产生一个疑问,调用了scheduleTraversals方法后,代码里只是将Runnable作为参数传递到了Choreographer的postCallback方法中,要想调用doTraversal方法,那必须要有某处执行这个Runnable。为了解清楚执行逻辑,请看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) {
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;
//编号6
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方法,而传递进来的delayMillis是0,所以dueTime = now,所以调用的是scheduleFrameLocked方法,接下来看它的调用代码
//方法
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on vsync.");
}
//是否在主线程
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
final long nextFrameTime = Math.max(
mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
}
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}
//方法
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
//方法 DisplayEventReceiver
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);
}
}
//方法 FrameDisplayEventReceiver类
private static native void nativeScheduleVsync(long receiverPtr);
可以发现最终调用的是native方法,到这里就没法跟下去了,换种思路,既然之前把Runnable放到了CallbackQueue中,见第4点的编号6注释,那么调用时机一定对应于从CallbackQueue取出Runnable,也就是CallbackQueue的extractDueCallbacksLocked方法,经过查找,可以得出是doCallbacks方法内调用了该方法( CallbackQueue 是Choreographer的内部类 ) ,而调用doCallbacks方法的是doFrame方法。(这里可以看到一个callbackType对应一个Runnable队列,mCallbackQueue[callbackType])
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
// We use "now" to determine when callbacks become due because it's possible
// for earlier processing phases in a frame to post callbacks that should run
// in a following phase, such as an input event that causes an animation to start.
final long now = System.nanoTime();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
.....
}
}
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
......
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
......
那么doFrame方法是哪里调用的呢?从之前得出了是调用FrameDisplayEventReceiver的native方法后就跟不下去了,那么进入这个类中可以发现它的 run 方法恰恰调用了doFrame方法,而官方对它其中的onVsync方法也有注释 Called when a vertical sync pulse is received ,说明这是底层回调用的。
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
Log.d(TAG, "Received vsync from secondary display, but we don't support "
+ "this case yet. Choreographer needs a way to explicitly request "
+ "vsync for a specific display to ensure it doesn't lose track "
+ "of its scheduled vsync.");
scheduleVsync();
return;
}
// Post the vsync event to the Handler.
long now = System.nanoTime();
if (timestampNanos > now) {
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;
//编号7
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);
}
从Handler的处理消息机制来看,我们知道一般如果传递了Runnable,会执行它的run方法,否则看你有没有指定Callback,有的话会执行你的Callback,没有则会调用handleMessage,所以 编号7 处,将this传递给了Message,后续将执行自己的run方法,继而执行doFrame方法,然后就会执行doCallbacks方法,然后会执行Runnable对象的run方法,run方法内又会执行doTraversals方法,这就开始刷新view咯
小结一下,FrameDisplayEventReceiver继承DisplayEventReceiver接受底层的VSync信号开始处理UI过程,VSync信号由SurfaceFlinger实现并定时发送。FrameDisplayEventReceiver收到信号后,调用onVsync方法组织消息发送到主线程处理。这个消息的内容主要就是run方法里面的doFrame了。FrameDsiplayEventReceiver之所以能收到信号,回调onVsync方法,可以理解为APP层在调用native方法nativeScheduleVsync时向底层注册了一个屏幕刷新信号监听事件,要不然底层怎么知道APP需要刷新数据呢?并且APP对底层是隐藏的,底层压根不知道APP的存在。
梳理一下,APP通过native方法向底层注册了下一帧的屏幕刷新界面,然后在每16.6ms的帧信号到来时,它就会回调onVsync刷新屏幕了
那么问题来了,是不是在16.6ms内或者是屏幕刷新前,我可以无限注册该监听事件,也就是一帧内是不是会注册很多重复监听
首先看段代码
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//编号 8
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//编号 9
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
......
}
首先看scheduleTraversals方法,它在向底层注册监听前,有一个mTraversalScheduled变量,该变量默认false,多次调用scheduleTraversals方法,只要mTraversalScheduled为false才会重复注册,而从doTraversal方法中可以看出只有接收到一帧信号是该变量才会重置为false。那为什么这么设计呢?查看performTraversals方法就可以知道,界面刷新调用了这个方法,方法内可以层层遍历View树的,需要刷新的View都会遍历到并刷新的,所以也就没有必要重复注册吧
接下来细心的人可能会问,在onVsync方法内的 编号7 处代码,这里会将刷新消息封装到Message里面放到主线程的MessageQueue中,而咱们的主线程也是一直在处理MessageQueue里面的消息的,同一时间只能处理一个Message,如果其它消息耗时,导致刷新消息在16.6ms内还没处理,该怎么办
小结
二、SurfaceView