Android系統(tǒng)從4.1(API 16)開(kāi)始加入Choreographer這個(gè)類來(lái)控制同步處理輸入(Input)刀诬、動(dòng)畫(Animation)、繪制(Draw)三個(gè)UI操作亲桦。其實(shí)UI顯示的時(shí)候每一幀要完成的事情只有這三種涮坐。如下圖是官網(wǎng)的相關(guān)說(shuō)明:
Choreographer接收顯示系統(tǒng)的時(shí)間脈沖(垂直同步信號(hào)-VSync信號(hào))己儒,在下一個(gè)frame渲染時(shí)控制執(zhí)行這些操作。
Choreographer中文翻譯過(guò)來(lái)是"舞蹈指揮"巾腕,字面上的意思就是優(yōu)雅地指揮以上三個(gè)UI操作一起跳一支舞面睛。這個(gè)詞可以概括這個(gè)類的工作,如果android系統(tǒng)是一場(chǎng)芭蕾舞尊搬,他就是Android UI顯示這出精彩舞劇的編舞叁鉴,指揮臺(tái)上的演員們相互合作,精彩演出佛寿。Google的工程師看來(lái)挺喜歡舞蹈的幌墓!
好了廢話不多說(shuō),下面讓我們來(lái)看看劇本是怎么設(shè)計(jì)的,Let's Read the fucking source code!
Choreographer的源碼位于android.view這個(gè)pakage中常侣,是view層框架的一部分蜡饵,Android studio里面搜一下就可以看到源碼了。
首先看看頭部的一些說(shuō)明胳施,大體了解一下這個(gè)類是干嘛的溯祸,有助于我們理解接下來(lái)的源碼。 和官網(wǎng)的文檔是一樣的舞肆,應(yīng)該就是用這個(gè)生成的焦辅,和上面一部分相比介紹了Choreographer的使用接口。開(kāi)發(fā)者可以使用Choreographer#postFrameCallback設(shè)置自己的callback與Choreographer交互胆绊,你設(shè)置的callCack會(huì)在下一個(gè)frame被渲染時(shí)觸發(fā)氨鹏。Callback有4種類型,Input压状、Animation仆抵、Draw,還有一種是用來(lái)解決動(dòng)畫啟動(dòng)問(wèn)題的种冬,將在下文介紹镣丑。這四種操作都是這么觸發(fā)的。
如下圖:
收到VSync信號(hào)后娱两,順序執(zhí)行3個(gè)操作莺匠,然后等待下一個(gè)信號(hào),再次順序執(zhí)行3個(gè)操作十兢。假設(shè)在第二個(gè)信號(hào)到來(lái)之前趣竣,所有的操作都執(zhí)行完成了,即Draw操作完成了旱物,那么第二個(gè)信號(hào)來(lái)到時(shí)遥缕,此時(shí)界面將會(huì)更新為第一frame的內(nèi)容,因?yàn)镈raw操作已經(jīng)完成了宵呛。否則界面將不會(huì)更新单匣,還是現(xiàn)實(shí)上一個(gè)frame的內(nèi)容,表示你丟幀了宝穗。丟幀是造成卡頓的原因户秤。如下圖:
第二個(gè)信號(hào)到來(lái)時(shí),Draw操作沒(méi)有按時(shí)完成逮矛,導(dǎo)致第三個(gè)時(shí)鐘周期內(nèi)顯示的還是第一幀的內(nèi)容鸡号。
注意文檔的最后一段話:
Each Looper thread has its own choreographer. Other threads can post callbacks to run on the choreographer but they will run on the Looper to which the choreographer belongs.*
每個(gè)線程都有自己的choreographer。
基本上的原理就是上面這樣须鼎,那么接下來(lái)我們通過(guò)源碼詳細(xì)地看一下細(xì)節(jié)是怎么實(shí)現(xiàn)的膜蠢。
首先先看看構(gòu)造函數(shù)堪藐。
構(gòu)造函數(shù)
private Choreographer(Looper looper) {
mLooper = looper;
mHandler = new FrameHandler(looper);
mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : 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();
}
}
這里做了幾個(gè)初始化操作,根據(jù)Looper對(duì)象生成挑围,Looper和線程是一對(duì)一的關(guān)系礁竞,對(duì)應(yīng)上面說(shuō)明里的每個(gè)線程對(duì)應(yīng)一個(gè)Choreographer。
1.初始化FrameHandler杉辙。接收處理消息模捂。
2.初始化FrameDisplayEventReceiver。FrameDisplayEventReceiver用來(lái)接收垂直同步脈沖蜘矢,就是VSync信號(hào)狂男,VSync信號(hào)是一個(gè)時(shí)間脈沖,一般為60HZ品腹,用來(lái)控制系統(tǒng)同步操作岖食,怎么同ChoreoGrapher一起工作的,將在下文介紹舞吭。
3.初始化mLastFrameTimeNanos(標(biāo)記上一個(gè)frame的渲染時(shí)間)以及mFrameIntervalNanos(幀率,fps泡垃,一般手機(jī)上為1s/60)。
4.初始化CallbackQueue羡鸥,callback隊(duì)列蔑穴,將在下一幀開(kāi)始渲染時(shí)回調(diào)。
我們首先看看FrameHandler和FrameDisplayEventReceiver的結(jié)構(gòu)惧浴。
FrameHandler
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:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
看上面的代碼存和,就是一個(gè)簡(jiǎn)單的Handler。處理3個(gè)類型的消息衷旅。
MSG_DO_FRAME:開(kāi)始渲染下一幀的操作
MSG_DO_SCHEDULE_VSYNC:請(qǐng)求Vsync信號(hào)
MSG_DO_SCHEDULE_CALLBACK:請(qǐng)求執(zhí)行callback
額捐腿,下面再細(xì)分一下,分別詳細(xì)看一下這三個(gè)步驟是怎么實(shí)現(xiàn)的柿顶。繼續(xù)看源碼吧茄袖。。九串。
FrameDisplayEventReceiver
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
public FrameDisplayEventReceiver(Looper looper) {
super(looper);
}
@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);
}
}
FrameDisplayEventReceiver繼承自DisplayEventReceiver接收底層的VSync信號(hào)開(kāi)始處理UI過(guò)程。VSync信號(hào)由SurfaceFlinger實(shí)現(xiàn)并定時(shí)發(fā)送寺鸥。FrameDisplayEventReceiver收到信號(hào)后猪钮,調(diào)用onVsync方法組織消息發(fā)送到主線程處理。這個(gè)消息主要內(nèi)容就是run方法里面的doFrame了胆建,這里mTimestampNanos是信號(hào)到來(lái)的時(shí)間參數(shù)烤低。
FrameHandler和FrameDisplayEventReceiver是怎么工作的呢?ChoreoGrapher的總體流程圖如下圖:
流程圖
以上是總體的流程圖:
1.PostCallBack,發(fā)起添加回調(diào)笆载,這個(gè)FrameCallBack將在下一幀被渲染時(shí)執(zhí)行扑馁。
2.AddToCallBackQueue,將FrameCallBack添加到回調(diào)隊(duì)列里面涯呻,等待時(shí)機(jī)執(zhí)行回調(diào)。每種類型的callback按照設(shè)置的執(zhí)行時(shí)間(dueTime)順序排序分別保存在一個(gè)單鏈表中腻要。
3.判斷FrameCallBack設(shè)定的執(zhí)行時(shí)間是否在當(dāng)前時(shí)間之后复罐,若是,發(fā)送MSG_DO_SCHEDULE_CALLBACK消息到主線程雄家,安排執(zhí)行doScheduleCallback效诅,安排執(zhí)行CallBack。否則直接跳到第4步趟济。
4.執(zhí)行scheduleFrameLocked乱投,安排執(zhí)行下一幀。
5.判斷上一幀是否已經(jīng)執(zhí)行顷编,若未執(zhí)行戚炫,當(dāng)前操作直接結(jié)束。若已經(jīng)執(zhí)行媳纬,根據(jù)情況執(zhí)行以下6双肤、7步。
6.若使用垂直同步信號(hào)進(jìn)行同步层宫,則執(zhí)行7.否則杨伙,直接跳到9。
7.若當(dāng)前線程是UI線程萌腿,則通過(guò)執(zhí)行scheduleVsyncLocked請(qǐng)求垂直同步信號(hào)限匣。否則,送MSG_DO_SCHEDULE_VSYNC消息到主線程毁菱,安排執(zhí)行doScheduleVsync米死,在主線程調(diào)用scheduleVsyncLocked。
8.收到垂直同步信號(hào)贮庞,調(diào)用FrameDisplayEventReceiver.onVsync()峦筒,發(fā)送消息到主線程,請(qǐng)求執(zhí)行doFrame窗慎。
9.執(zhí)行doFrame物喷,渲染下一幀。
主要的工作在doFrame中遮斥,接下來(lái)我們具體看看doFrame函數(shù)都干了些什么峦失。
從名字看很容易理解doFrame函數(shù)就是開(kāi)始進(jìn)行下一幀的顯示工作。好了以下源代碼又來(lái)了术吗,我們一行一行分析一下吧尉辑。
doFrame
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) { //判斷是否有callback需要執(zhí)行,mFrameScheduled會(huì)在postCallBack的時(shí)候置為true,一次frame執(zhí)行時(shí)置為false
return; // no work to do
}
\\打印跳frame時(shí)間
if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
mDebugPrintNextFrameTimeDelta = false;
Log.d(TAG, "Frame time delta: "
+ ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
}
//設(shè)置當(dāng)前frame的Vsync信號(hào)到來(lái)時(shí)間
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();//實(shí)際開(kāi)始執(zhí)行當(dāng)前frame的時(shí)間
//時(shí)間差
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
//時(shí)間差大于一個(gè)時(shí)鐘周期较屿,認(rèn)為跳frame
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//跳frame數(shù)大于默認(rèn)值隧魄,打印警告信息卓练,默認(rèn)值為30
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
//計(jì)算實(shí)際開(kāi)始當(dāng)前frame與時(shí)鐘信號(hào)的偏差值
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.");
}
//修正偏差值,忽略偏差购啄,為了后續(xù)更好地同步工作
frameTimeNanos = startNanos - lastFrameOffset;
}
//若時(shí)間回溯襟企,則不進(jìn)行任何工作,等待下一個(gè)時(shí)鐘信號(hào)的到來(lái)
//這里為什么會(huì)發(fā)生時(shí)間回溯我沒(méi)搞明白闸溃,大概是未知時(shí)鐘錯(cuò)誤引起整吆?注釋里說(shuō)的maybe 好像不太對(duì)
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.");
}
//請(qǐng)求下一次時(shí)鐘信號(hào)
scheduleVsyncLocked();
return;
}
//記錄當(dāng)前frame信息
mFrameInfo.setVsync(intendedFrameTimeNanos,frameTimeNanos);
mFrameScheduled = false;
//記錄上一次frame開(kāi)始時(shí)間,修正后的
mLastFrameTimeNanos = frameTimeNanos;
}
try {
//執(zhí)行相關(guān)callBack
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
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 {
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.");
}
}
大部分內(nèi)容都在上面的注釋中說(shuō)明了辉川,大概是以下的流程:
總結(jié)起來(lái)其實(shí)主要是兩個(gè)操作:
1.設(shè)置當(dāng)前frame的啟動(dòng)時(shí)間表蝙。
判斷是否跳幀,若跳幀修正當(dāng)前frame的啟動(dòng)時(shí)間到最近的VSync信號(hào)時(shí)間乓旗。如果沒(méi)跳幀府蛇,當(dāng)前frame啟動(dòng)時(shí)間直接設(shè)置為當(dāng)前VSync信號(hào)時(shí)間。修正完時(shí)間后屿愚,無(wú)論當(dāng)前frame是否跳幀汇跨,使得當(dāng)前frame的啟動(dòng)時(shí)間與VSync信號(hào)還是在一個(gè)節(jié)奏上的,可能可能延后了一到幾個(gè)周期妆距,但是節(jié)奏點(diǎn)還是吻合的穷遂。
如下圖所示是時(shí)間修正的一個(gè)例子,
由于第二個(gè)frame執(zhí)行超時(shí)娱据,第三個(gè)frame實(shí)際啟動(dòng)時(shí)間比第三個(gè)VSync信號(hào)到來(lái)時(shí)間要晚蚪黑,因?yàn)檫@時(shí)候延時(shí)比較小,沒(méi)有超過(guò)一個(gè)時(shí)鐘周期中剩,系統(tǒng)還是將frameTimeNanos3傳給回調(diào)忌穿,回調(diào)拿到的時(shí)間和VSync信號(hào)同步。
再來(lái)看看下圖:
由于第二個(gè)frame執(zhí)行時(shí)間超過(guò)2個(gè)時(shí)鐘周期结啼,導(dǎo)致第三個(gè)frame延后執(zhí)行時(shí)間大于一個(gè)時(shí)鐘周期掠剑,系統(tǒng)認(rèn)為這時(shí)候影響較大,判定為跳幀了郊愧,將第三個(gè)frame的時(shí)間修正為frameTimeNanos4,比VSync真正到來(lái)的時(shí)間晚了一個(gè)時(shí)鐘周期朴译。
時(shí)間修正,既保證了doFrame操作和VSync保持同步節(jié)奏属铁,又保證實(shí)際啟動(dòng)時(shí)間與記錄的時(shí)間點(diǎn)相差不會(huì)太大眠寿,便于同步及分析。
2.順序執(zhí)行callBack隊(duì)列里面的callback.
然后接下來(lái)看看doCallbacks的執(zhí)行過(guò)程:
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;
// Update the frame time if necessary when committing the frame.
// We only update the frame time if we are more than 2 frames late reaching
// the commit phase. This ensures that the frame time which is observed by the
// callbacks will always increase from one frame to the next and never repeat.
// We never want the next frame's starting frame time to end up being less than
// or equal to the previous frame's commit frame time. Keep in mind that the
// next frame has most likely already been scheduled by now so we play it
// safe by ensuring the commit time is always at least one frame behind.
if (callbackType == Choreographer.CALLBACK_COMMIT) {
final long jitterNanos = now - frameTimeNanos;
Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
if (jitterNanos >= 2 * mFrameIntervalNanos) {
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
+ mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
+ " ms which is more than twice the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Setting frame time to " +(lastFrameOffset * 0.000001f)
+ " ms in the past.");
mDebugPrintNextFrameTimeDelta = true;
}
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
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);
}
}
callback的類型有以下4種红选,除了文章一開(kāi)始提到的3中外澜公,還有一個(gè)CALLBACK_COMMIT姆另。
CALLBACK_INPUT:輸入
CALLBACK_ANIMATION:動(dòng)畫
CALLBACK_TRAVERSAL:遍歷喇肋,執(zhí)行measure坟乾、layout、draw
CALLBACK_COMMIT:遍歷完成的提交操作蝶防,用來(lái)修正動(dòng)畫啟動(dòng)時(shí)間
然后看上面的源碼甚侣,分析一下每個(gè)callback的執(zhí)行過(guò)程:
1.callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS);得到執(zhí)行時(shí)間在當(dāng)前時(shí)間之前的所有CallBack,保存在單鏈表中间学。每種類型的callback按執(zhí)行時(shí)間先后順序排序分別存在一個(gè)單鏈表里面殷费。為了保證當(dāng)前callback執(zhí)行時(shí)新post進(jìn)來(lái)的callback在下一個(gè)frame時(shí)才被執(zhí)行,這個(gè)地方extractDueCallbacksLocked會(huì)將需要執(zhí)行的callback和以后執(zhí)行的callback斷開(kāi)變成兩個(gè)鏈表低葫,新post進(jìn)來(lái)的callback會(huì)被放到后面一個(gè)鏈表中详羡。當(dāng)前frame只會(huì)執(zhí)行前一個(gè)鏈表中的callback,保證了在執(zhí)行callback時(shí)嘿悬,如果callback中Post相同類型的callback实柠,這些新加的callback將在下一個(gè)frame啟動(dòng)后才會(huì)被執(zhí)行。
2.接下來(lái)善涨,看一大段注釋窒盐,如果類型是CALLBACK_COMMIT,并且當(dāng)前frame渲染時(shí)間超過(guò)了兩個(gè)時(shí)鐘周期钢拧,則將當(dāng)前提交時(shí)間修正為上一個(gè)垂直同步信號(hào)時(shí)間蟹漓。為了保證下一個(gè)frame的提交時(shí)間和當(dāng)前frame時(shí)間相差為一且不重復(fù)。
這個(gè)地方注釋挺難看懂源内,實(shí)際上這個(gè)地方CALLBACK_COMMIT是為了解決ValueAnimator的一個(gè)問(wèn)題而引入的葡粒,主要是解決因?yàn)楸闅v時(shí)間過(guò)長(zhǎng)導(dǎo)致動(dòng)畫時(shí)間啟動(dòng)過(guò)長(zhǎng),時(shí)間縮短姿锭,導(dǎo)致跳幀塔鳍,這里修正動(dòng)畫第一個(gè)frame開(kāi)始時(shí)間延后來(lái)改善,這時(shí)候才表示動(dòng)畫真正啟動(dòng)呻此。為什么不直接設(shè)置當(dāng)前時(shí)間而是回溯一個(gè)時(shí)鐘周期之前的時(shí)間呢轮纫?看注釋,這里如果設(shè)置為當(dāng)前frame時(shí)間焚鲜,因?yàn)閯?dòng)畫的第一個(gè)frame其實(shí)已經(jīng)繪制完成,第二個(gè)frame這時(shí)候已經(jīng)開(kāi)始了忿磅,設(shè)置為當(dāng)前時(shí)間會(huì)導(dǎo)致這兩個(gè)frame時(shí)間一樣撩扒,導(dǎo)致沖突搓谆。詳細(xì)情況請(qǐng)看官方針對(duì)這個(gè)問(wèn)題的修改泉手。Fix animation start jank due to expensive layout operations.
如下圖所示:
比如說(shuō)在第二個(gè)frame開(kāi)始執(zhí)行時(shí),開(kāi)始渲染動(dòng)畫的第一個(gè)畫面憋飞,第二個(gè)frame執(zhí)行時(shí)間超過(guò)了兩個(gè)時(shí)鐘周期搀崭,Draw操作執(zhí)行結(jié)束后瘤睹,這時(shí)候完成了動(dòng)畫第一幀的渲染轰传,動(dòng)畫實(shí)際上還沒(méi)開(kāi)始,但是時(shí)間已經(jīng)過(guò)了兩個(gè)時(shí)鐘周期恕曲,后面動(dòng)畫實(shí)際執(zhí)行時(shí)間將會(huì)縮短一個(gè)時(shí)鐘周期佩谣。這時(shí)候系統(tǒng)通過(guò)修正commit時(shí)間到frameTimeNanos的上一個(gè)VSync信號(hào)時(shí)間,即完成動(dòng)畫第一幀渲染之前的VSync信號(hào)到來(lái)時(shí)間调鬓,修正了動(dòng)畫啟動(dòng)時(shí)間,保證動(dòng)畫執(zhí)行時(shí)間的正確性虹脯。
3.接下來(lái)就是調(diào)用c.run(frameTimeNanos);執(zhí)行回調(diào)。
例如暇榴,你可以寫一個(gè)自定義的FPSFrameCallback繼承自Choreographer.FrameCallback蔼紧,實(shí)現(xiàn)里面的doFrame方法。
public class FPSFrameCallback implements Choreographer.FrameCallback{
@Override
public void doFrame(long frameTimeNanos){
//do something
}
}
通過(guò)
Choreographer.getInstance().postFrameCallback(new FPSFrameCallback());
把你的回調(diào)添加到Choreographer之中查吊,那么在下一個(gè)frame被渲染的時(shí)候就會(huì)回調(diào)你的callback,執(zhí)行你定義的doFrame操作逻卖,這時(shí)候你就可以獲取到這一幀的開(kāi)始渲染時(shí)間并做一些自己想做的事情了。
開(kāi)源組件Tiny Dancer就是根據(jù)這個(gè)原理獲取每一幀的渲染時(shí)間盗迟,繼而分析實(shí)現(xiàn)獲取設(shè)備的當(dāng)前幀率的。有興趣的人可以查看怕磨。
Tiny Dancer
好了肠鲫,關(guān)于Choreographer的分析到此結(jié)束。希望對(duì)你有幫助渣锦。