Android?Choreographer源碼詳細(xì)分析
一、首先介紹一些基礎(chǔ)知識(shí)
1.刷新率(Refresh Rate):
刷新率代表屏幕在一秒內(nèi)刷新屏幕的次數(shù),用赫茲來(lái)表示。赫茲是頻率的單位,一秒震動(dòng)的次數(shù)。這個(gè)刷新率取決于硬件固定的參數(shù)。這個(gè)值一般是60Hz。即每16.66ms刷新一次屏幕。
2.幀速率(Frame Rate):
幀速率代表了GPU在一秒內(nèi)繪制操作的幀數(shù)。比如30FPS、60FPS。Frame Per Second。
3.如果兩個(gè)設(shè)備獨(dú)立運(yùn)行,如果刷新率和幀速率不同步,會(huì)引發(fā)以下兩種問(wèn)題。
如果幀速率高于刷新率,制作的頻率大于展示的頻率,會(huì)導(dǎo)致屏幕圖像的展示的跳躍,跳幀的現(xiàn)象。
如果幀速率小于刷新率,制作的頻率小于展示的頻率,會(huì)導(dǎo)致屏幕圖像的展示的中斷,掉幀的現(xiàn)象。
4.android為了解決上面的問(wèn)題,在4.1版本中引入了Projectbuffer.
ProjectBuffer對(duì)Android Display系統(tǒng)進(jìn)行了重構(gòu),引入了三個(gè)核心元素,即Vsync,TripleBuffer和Choreographer。
其中Vsync是Vertical Synchronization 垂直同步是縮寫(xiě)。是一種在PC上已經(jīng)很早就廣泛使用的技術(shù)。
引入是Vsync來(lái)進(jìn)行控制CPUGPU的繪制和屏幕刷新同步進(jìn)行。
而編舞者choreography的引入,主要是配合Vsync,給上層App的渲染提供一個(gè)穩(wěn)定的時(shí)機(jī)。Vsync到來(lái)的時(shí)候,Choreographer可以根據(jù)Vsync信號(hào),統(tǒng)一管理應(yīng)用的輸入、動(dòng)畫(huà)、繪制等任務(wù)的執(zhí)行情況。Choreographer就像一個(gè)指揮家一樣,來(lái)把控著UI的繪制,所以取名編舞者。
二、android源碼中Choreographer是如何運(yùn)行
1.首先在ViewRootImpl構(gòu)造函數(shù)中創(chuàng)建了Choreographer對(duì)象
public ViewRootImpl(Context context, Display display) { mChoreographer = Choreographer.getInstance(); } public static Choreographer getInstance() { return sThreadInstance.get(); }
當(dāng)調(diào)用get時(shí),如果為null,會(huì)調(diào)用initialValue()方法。并把Choreographer實(shí)例和ThreadLocal綁定。
private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() { //因?yàn)楹竺鏁?huì)用到handler通訊,所以必須有一個(gè)Looper循環(huán) Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP); //如果是主線程,則把choreographer賦值給mMainInstance if (looper == Looper.getMainLooper()) { mMainInstance = choreographer; } return choreographer; } };
2.看Choreographer構(gòu)造函數(shù)
mLastFrameTimeNanos:記錄上一幀繪制的時(shí)間。
mFrameIntervalNanos:屏幕繪制一幀的時(shí)間間隔,這個(gè)是納秒值。如果屏幕刷新率是60Hz,那么刷新一幀的時(shí)間間隔就是16.66.......毫秒。
private Choreographer(Looper looper, int vsyncSource) { // 傳一個(gè)Looper進(jìn)來(lái), mLooper = looper; //用來(lái)處理消息的。 mHandler = new FrameHandler(looper); //USE_VSYNC 是否使用Vsync //boolean USE_VSYNC = SystemProperties.getBoolean("debug.choreographer.vsync", true); mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null; //上一幀繪制的時(shí)間 mLastFrameTimeNanos = Long.MIN_VALUE; //1秒是1000毫秒,1毫秒是1000微秒,1微秒是1000納秒 //1秒就是1*1000*1000*1000=10的九次方納秒 //繪制一幀的時(shí)間間隔----納秒。如果是60Hz,那么刷新一幀展示的時(shí)間就是16.66毫秒。 mFrameIntervalNanos = (long)(1000000000 / getRefreshRate()); //初始化回調(diào)隊(duì)列,后面會(huì)從這個(gè)回調(diào)隊(duì)列中取出Runnable執(zhí)行run方法。 mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1]; for (int i = 0; i <= CALLBACK_LAST; i++) { mCallbackQueues[i] = new CallbackQueue(); } }
獲取屏幕的刷新率:
//屏幕的刷新率,一秒鐘可以刷新屏幕多少次,通常是60Hz private static float getRefreshRate() { DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( Display.DEFAULT_DISPLAY); return di.getMode().getRefreshRate(); }
3.初始化工作完成,那么Choreographer是怎么跑起來(lái)的,入口函數(shù)在哪?
對(duì)于UI繪制來(lái)說(shuō)是入口在RootViewImpl的scheduleTraversals()方法中。
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; //發(fā)送一個(gè)屏障消息 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //注意第一個(gè)參數(shù)CALLBACK_TRAVERSAL,回調(diào)函數(shù)的類(lèi)型。 //mTraversalRunnable 回調(diào)函數(shù)要執(zhí)行的runnable。 //第三個(gè)參數(shù)token,傳了一個(gè)null mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
//第一個(gè)參數(shù)callbackType 有五種類(lèi)型,這幾個(gè)回調(diào)是有順序的。
1.CALLBACK_INPUT 輸入回調(diào),首先運(yùn)行
2.CALLBACK_ANIMATION 動(dòng)畫(huà)回調(diào),這個(gè)在將動(dòng)畫(huà)原理的時(shí)候,會(huì)看到
3.CALLBACK_INSETS_ANIMATION inset和update相關(guān)的動(dòng)畫(huà),運(yùn)行在上面兩個(gè)回調(diào)之后,
4.CALLBACK_TRAVERSAL 遍歷回調(diào),用于處理布局和繪制
5.CALLBACK_COMMIT Commit回調(diào),在Traversal繪制回調(diào)之后。
接下來(lái)看postCallbackDelayedInternal方法
第二個(gè)參數(shù)就是上面的mTraversalRunnable。
第四個(gè)參數(shù)延遲的時(shí)間,這里延遲時(shí)間是0,沒(méi)有延遲
所以這個(gè)方法走if判斷的第一個(gè)分支
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; //將runnable加入回調(diào)隊(duì)列 mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); 上面?zhèn)鬟^(guò)來(lái)的delayMillis是0,所以走第一個(gè)分支。 if (dueTime <= now) { scheduleFrameLocked(now); } else { //如果有延遲,則發(fā)送一個(gè)延遲的異步消息。這種消息在handler同步屏障文章中介紹過(guò) Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } } }
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; //如果使用垂直同步 if (USE_VSYNC) { //判斷是否運(yùn)行在主線程,如果是則直接調(diào)用scheduleVsyncLocked() //如果運(yùn)行在子線程則通過(guò)發(fā)送handler 的方式也會(huì)調(diào)用到scheduleVsyncLocked() if (isRunningOnLooperThreadLocked()) {//Looper.myLooper() == mLooper 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); Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } }
scheduleVsyncLocked()方法。
private void scheduleVsyncLocked() { //調(diào)用父類(lèi) DisplayEventReceiver的方法 mDisplayEventReceiver.scheduleVsync(); }
在scheduleVsync()方法中會(huì)調(diào)用nativeScheduleVsync,這是一個(gè)native方法,在native層執(zhí)行完畢后會(huì)回調(diào)到j(luò)ava層的方法dispatchVsync()
scheduleVsync:向native層去請(qǐng)求一個(gè)Vsync信號(hào)。
dispatchVsync:請(qǐng)求到Vsync信號(hào)后,執(zhí)行Java層的UI繪制和渲染邏輯。
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 { // 調(diào)用native 方法 //調(diào)用Native方法請(qǐng)求一個(gè)Vsync信號(hào),然后會(huì)從native層回調(diào)java層的dispatchVsync方法 nativeScheduleVsync(mReceiverPtr); } }
timestampNanos:從Native層傳遞過(guò)來(lái)的一個(gè)時(shí)間戳,Vsync從native層發(fā)出的時(shí)間。
// Called from native code. //從native層回調(diào)java層的dispatchVsync方法 private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) { onVsync(timestampNanos, physicalDisplayId, frame); }
在這又發(fā)送了一個(gè)異步消息,并且 Message.obtain(mHandler, this);第二個(gè)參數(shù)是一個(gè)callBack回調(diào)。所以沒(méi)有handler的情況下,會(huì)執(zhí)行這個(gè)回調(diào)函數(shù)。但是傳的是this,所以就會(huì)執(zhí)行this的run方法。這個(gè)this就是FrameDisplayEventReceiver的實(shí)例,在Choreographer的構(gòu)造函數(shù)中初始化的。
public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { mTimestampNanos = timestampNanos; mFrame = frame; //得到message 添加了一個(gè)回調(diào)函數(shù),this,則會(huì)調(diào)用run方法 Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); }
在FrameDisplayEventReceiver的run方法中,調(diào)用的doFrame方法
@Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); }
void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { if (!mFrameScheduled) { return; // no work to do } //sync信號(hào)發(fā)出的時(shí)間, long intendedFrameTimeNanos = frameTimeNanos; //當(dāng)前的時(shí)間 startNanos = System.nanoTime(); //兩者相減得到的時(shí)間差,就是底層消息通訊和回調(diào)所消耗的時(shí)間 final long jitterNanos = startNanos - frameTimeNanos; //如果這個(gè)時(shí)間差大于了一幀的時(shí)間間隔。 if (jitterNanos >= mFrameIntervalNanos) { //計(jì)算跳過(guò)了多少幀 final long skippedFrames = jitterNanos / mFrameIntervalNanos; //注意下面這行日子,如果跳幀大于30幀,系統(tǒng)會(huì)打印下面這行l(wèi)og,在主線程做了太多工作,會(huì)造成UI卡頓。 if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread."); } //取模,得到的值就是一幀多出來(lái)的時(shí)間 final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; } //用當(dāng)前時(shí)間減去多出來(lái)的時(shí)間,就是下一幀要繪制的時(shí)間 //進(jìn)行繪制時(shí)間的修正,保證每一次的繪制時(shí)間間隔都是mFrameIntervalNanos frameTimeNanos = startNanos - lastFrameOffset; } //如果底層傳過(guò)來(lái)的時(shí)間,小于上一幀繪制的時(shí)間,正常情況下,frameTimeNanos都是大于上一幀繪制的時(shí)間的。 if (frameTimeNanos < mLastFrameTimeNanos) { //跳過(guò)本次的繪制,請(qǐng)求下一幀的時(shí)間 scheduleVsyncLocked(); return; } //以上的判斷,都是為了控制繪制的頻率。 if (mFPSDivisor > 1) { long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) { scheduleVsyncLocked(); return; } } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); //重置標(biāo)志位,可以再次進(jìn)入scheduleFrameLocked mFrameScheduled = false; //將底層傳過(guò)來(lái)的時(shí)間,記錄為本次繪制的時(shí)間,也就是下一幀傳過(guò)來(lái)時(shí),上一幀繪制的時(shí)間。 mLastFrameTimeNanos = frameTimeNanos; } try { AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); //根據(jù)Choreographer的CallBack類(lèi)型,進(jìn)行callBack的回調(diào)。 //輸入 doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); //動(dòng)畫(huà) doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); //界面繪制 doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); //commit doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
這個(gè)是很重要的一個(gè)方法。
通過(guò)這個(gè)方法中的邏輯能夠看出:Choreographer控制App層UI繪制的節(jié)奏和頻率。
然后會(huì)按順序執(zhí)行一些列的doCallBacks函數(shù)。
首先會(huì)根據(jù)callbackType,從鏈表中取出CallBackRecord。然后再遍歷CallBackRecord,調(diào)用他的run方法。
void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { final long now = System.nanoTime(); //根據(jù)callbacktype,從鏈表中拿到 CallbackRecord callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true; for (CallbackRecord c = callbacks; c != null; c = c.next) { //執(zhí)行CallbackRecord的run方法 c.run(frameTimeNanos); } } }
根據(jù)token來(lái)進(jìn)行區(qū)分是FrameCallback類(lèi)型還是Runnable。
主要這里的token傳進(jìn)來(lái)的是null,所以會(huì)執(zhí)行else分支。
這個(gè)action就是mTraversalRunnable,調(diào)用mTraversalRunnable的run方法。
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
public void run(long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); } }
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
在它的run方法中執(zhí)行了doTraversal()。
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; //刪除屏障消息 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //調(diào)用測(cè)量、布局和繪制方法 performTraversals(); } }
performTraversals()方法中就會(huì)調(diào)用
performMeasure、performLayout、performDraw,對(duì)View進(jìn)行測(cè)量、布局、和繪制。
到此這篇關(guān)于Android Choreographer源碼詳細(xì)分析的文章就介紹到這了,更多相關(guān)Android Choreographer內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android實(shí)現(xiàn)百度地圖自定義彈出窗口功能
這篇文章主要介紹了android實(shí)現(xiàn)百度地圖自定義彈出窗口的功能,大家參考使用吧2013-11-11Android音頻處理之通過(guò)AudioRecord去保存PCM文件進(jìn)行錄制,播放,停止,刪除功能
這篇文章主要介紹了Android音頻處理之通過(guò)AudioRecord去保存PCM文件進(jìn)行錄制,播放,停止,刪除功能的相關(guān)資料,需要的朋友可以參考下2016-11-11Kotlin引用其他xml的view對(duì)象過(guò)程詳解
這篇文章主要介紹了Kotlin中如何引用其他xml中的view對(duì)象,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-02-02android教程之使用popupwindow創(chuàng)建菜單示例
這篇文章主要介紹了android使用popupwindow創(chuàng)建菜單的示例,需要的朋友可以參考下2014-02-02android 對(duì)話框彈出位置和透明度的設(shè)置具體實(shí)現(xiàn)方法
在android中我們經(jīng)常會(huì)用AlertDialog來(lái)顯示對(duì)話框。通過(guò)這個(gè)對(duì)話框是顯示在屏幕中心的。但在某些程序中,要求對(duì)話框可以顯示在不同的位置。2013-07-07Android實(shí)現(xiàn)搜索保存歷史記錄功能
這篇文章主要介紹了Android實(shí)現(xiàn)搜索保存歷史記錄功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05Material Design系列之Behavior實(shí)現(xiàn)支付密碼彈窗和商品屬性選擇效果
這篇文章主要為大家詳細(xì)介紹了Material Design系列之Behavior實(shí)現(xiàn)支付密碼彈窗和商品屬性選擇效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09