android在不一樣的版本都會優化「UI的流暢性」問題,可是直到在android 4.1版本中作了有效的優化,這就是Project Butter。android
Project Butter加入了三個核心元素:VSYNC、Triple Buffer和Choreographer。其中,VSYNC是理解Project Buffer的核心。VSYNC是Vertical Synchronization的縮寫 也就是「垂直同步」bash
檢測應用卡頓的方案,Android系統每隔16.6ms發出VSYNC信號,來通知界面進行輸入、動畫、繪製等動做,每一次同步的週期爲16.6ms,表明一幀的刷新頻率,理論上來講兩次回調的時間週期應該在16.6ms,若是超過了16.6ms咱們則認爲發生了卡頓,利用兩次回調間的時間週期來判斷是否發生卡頓 這個方案的原理主要是經過Choreographer類設置它的FrameCallback函數,當每一幀被渲染時會觸發回調FrameCallback, FrameCallback回調void doFrame (long frameTimeNanos)函數。一次界面渲染會回調doFrame方法,若是兩次doFrame之間的間隔大於16.6ms說明發生了卡頓。app
監控應用的流暢度通常都是經過Choreographer類的postFrameCallback方法註冊一個VSYNC回調事件ide
public static void start(final Builder builder) {
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
long lastFrameTimeNanos = 0;
long currentFrameTimeNanos = 0;
@Override
public void doFrame(long frameTimeNanos) {
if (lastFrameTimeNanos == 0) {
lastFrameTimeNanos = frameTimeNanos;
LogMonitor.getInstance().setFrequency(builder.frame * 17 / 2);
if (builder.targetPackageName != null) {
LogMonitor.getInstance().setTargetPackageName(builder.targetPackageName);
}
LogMonitor.getInstance().setDumpListener(builder.onDumpListener);
}
currentFrameTimeNanos = frameTimeNanos;
skipFrameCount = skipFrameCount(lastFrameTimeNanos, currentFrameTimeNanos, deviceRefreshRateMs);
LogMonitor.getInstance().setFrame(skipFrameCount);
if (LogMonitor.getInstance().isMonitor()) {
LogMonitor.getInstance().removeMonitor();
}
LogMonitor.getInstance().startMonitor();
lastFrameTimeNanos = currentFrameTimeNanos;
Choreographer.getInstance().postFrameCallback(this);
}
});
}
複製代碼
postFrameCallback(FrameCallback callback)的源碼函數
public void postFrameCallback(FrameCallback callback) {
postFrameCallbackDelayed(callback, 0);
}
......
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
postCallbackDelayedInternal(CALLBACK_ANIMATION,
callback, FRAME_CALLBACK_TOKEN, delayMillis);
}
複製代碼
postFrameCallback最終調用了postCallbackDelayedInternal()方法,咱們在跟蹤進去這個方法oop
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;
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方法中首選經過 mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);將咱們註冊的回調接口添加到mCallbackQueues隊列中。每種類型的callback按照設置的執行時間(dueTime)順序排序分別保存在一個單鏈表中。post
以後判斷執行時間是否爲當前時間,若是是直接調用scheduleFrameLocked(now);不然發送一個MSG_DO_SCHEDULE_CALLBACK一個消息,其實發送信息最後也是調用scheduleFrameLocked(now)方法,因此咱們直接看這個方法的代碼優化
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
if (USE_VSYNC) {//默認爲true
if (DEBUG_FRAMES) {
Log.d(TAG, "Scheduling next frame on 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.
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);
}
}
}
複製代碼
USE_VSYNC 默認是 true,表示默認開啓垂直同步動畫
private static final boolean USE_VSYNC = SystemProperties.getBoolean(
"debug.choreographer.vsync", true);
複製代碼
scheduleVsyncLocked方法的代碼ui
private void scheduleVsyncLocked() {
mDisplayEventReceiver.scheduleVsync();
}
......
/**
* Schedules a single vertical sync pulse to be delivered when the next
* display frame begins.
*/
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);
}
}
複製代碼
最終經過調native方法nativeScheduleVsync(mReceiverPtr)向底層註冊咱們的回調事件。 到此咱們向系統註冊「垂直同步」事件的流程已經結束。全部的流程以下:
接收VSYNS信號
VSYNC信號怎麼同步到咱們的代碼的呢,Java 層接收 VSYNC 的入口是 dispatchVsync(),也就是說每當系統底層產生一個VSYNC信號,系統都會回調這個方法。
// Called from native code.
@SuppressWarnings("unused")
private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
onVsync(timestampNanos, builtInDisplayId, frame);
}
複製代碼
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);
}
}
複製代碼
onVsync方法中,Message.obtain(mHandler, this) 因此 msg.callback 是 this,最後會調用到 msg.callback.run(),也就是 FrameDisplayEventReceiver run(),進入 doFrame() mTimestampNanos,它是來自 onVsync 的 timestampNanos 參數,表明產生 VSYNC 的時間
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {
return; // no work to do
}
if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
mDebugPrintNextFrameTimeDelta = false;
Log.d(TAG, "Frame time delta: "
+ ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
}
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();//正真開始的時間
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
// 時間差除以每幀時間間隔,來計算丟掉了幾幀。其中mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());通常刷新率爲60,時間間隔爲16.6ms
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.");
}
// 取餘數,做爲幀偏移時間
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
+ "which is more than the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Skipping " + skippedFrames + " frames and setting frame "
+ "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
}
frameTimeNanos = startNanos - lastFrameOffset;
}
if (frameTimeNanos < mLastFrameTimeNanos) {
if (DEBUG_JANK) {
Log.d(TAG, "Frame time appears to be going backwards. May be due to a "
+ "previously skipped frame. Waiting for next vsync.");
}
scheduleVsyncLocked();
return;
}
mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
}
//執行對應的callBack
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);
}
if (DEBUG_FRAMES) {
final long endNanos = System.nanoTime();
Log.d(TAG, "Frame " + frame + ": Finished, took "
+ (endNanos - startNanos) * 0.000001f + " ms, latency "
+ (startNanos - frameTimeNanos) * 0.000001f + " ms.");
}
}
複製代碼
若是你平時注意卡頓的日誌信息,那麼下面這個段log就不會陌生了
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
複製代碼
SKIPPED_FRAME_WARNING_LIMIT的默認值是30,也就說當咱們的程序卡頓大於30時會打印這條log信息 doFrame()方法最後調用doCallbacks()來處理用戶輸入,動畫,繪製等UI操做。
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); 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); } } 複製代碼
extractDueCallbacksLocked 是取出執行時間在當前時間以前的全部 CallbackRecord,CallbackRecord 是一個鏈表,而後遍歷 callbacks 執行 run 方法
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}
複製代碼
若是們經過postFrameCallback(FrameCallback)註冊回調事件,下一次 Choreographer doFrame 時就會調用 FrameCallback.doFrame,還記得剛開始咱們註冊FrameCallback是系統爲封裝FrameCallback的類型嗎 正是FRAME_CALLBACK_TOKEN,所以這裏會走 ((FrameCallback)action).doFrame(frameTimeNanos);,爲何咱們每次都須要註冊一個下呢,這是由於每次「垂直同步」都會刪除調用的註冊事件。 若是這個CallbackRecord是view動畫或繪製就會調用((Runnable)action).run();
下面是收到VSYNC的流程圖