android view的繪製中,View繪製的時間如何和vsync屏幕刷新頻率保持同步的?
窗口被激活,viewroot通過調用requestLayout然後scheduleTraversals和performTraversals,如果這個繪製時間和vsync的時間這兩個值不一致,不論View繪製的快慢,都會導致丟幀,那android是如何保持這兩者時間同步的呢?
最近剛好又研究了下這一塊,沒有研究各位那麼深入,對於底層的SurfaceFlinger還沒有看,主要集中在ViewRootImpl和Choreographer,以下是我的理解:1、ViewRootImpl
ViewRootImpl怎來的,大家可以看看ActivityThread裡面的handleResumeActivity,拋開anr這些系統的窗口等,理論上一個APP應該是一個ViewRootImpl
在WindowManagerGlobal的addView會
root = new ViewRootImpl(view.getContext(), display);
root.setView(view, wparams, panelParentView);
也就是說, ViewRootImpl的mView其實是DecorView.而DecorView是一個activity中最外層的view了,其實是繼承的FrameLayout。
而對於View不論是Invalidate還是requestlayout,這些重繪開始的地方都是ViewRootImpl的scheduleTraversals,在scheduleTraversals中,
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
有這樣一行代碼,這個mTraversalRunnable就是調用了View measure,layout,draw的非常經典的performTraversals方法。
而postCallback中做的事情主要就是根據時間看這個啥時候執行,如果要立即執行了就添加到一個callback隊列中然後去請求VSYNC(這裡只考慮有VSYNC的場景)。如果不需要立即執行就sendmessage做一個隊列排隊去。
Choreographer是對VSYNC做了監聽的,當有VSYNC消息的時候會執行onVsync,最終走到Choreographer的doFrame,在這裡會將從callback隊列中取出runnable進行繪製。
所以對於當用戶手動去invalidate,或者requestlayout的情況,還是會通過Choreographer對這些請求做排隊處理,所有繪製的地方都會等待VSYNC的消息回調再去做相應的繪製。所以就不會出現,用戶要求去繪製,但是這個時機和VSYNC不同步的問題了。
我的理解就是這樣,如果有問題歡迎指出!
參考文章:
Android動畫執行過程源碼分析
Android系統Choreographer機制實現過程
謝邀,這個問題有點深,以下只是我的一點淺顯看法,歡迎批評指正。
引入Vsync的作用是保證計算和刷新的同步。Android的一個顯示周期過程可以極簡為以下及部分:- CPU+GPU繪製幀數據,繪製結束的數據存放在緩衝區BufferQueue中
- SurfaceFlinger從BufferQueue中取數據並計算
- Choreographer完成最終的繪製
這三個過程都需要獲取Vsync,其中SurfaceFlinger和Choreographer是去主動捕獲Vsync,App啟動CPU繪製的Vsync是通過向SurfaceFlinger請求獲取。題主說的View繪製快慢是第一部分的工作,現在Android的繪製採用的是三緩衝(&>=4.1),如果第一步的繪製速度較快,數據會存儲在BufferQueue中,但此時CPU因為沒有請求到Vsync,不會開始下一幀的繪製,也就不存在繪製速度快導致的丟幀情況。
最近很忙沒什麼時間答題,這類問題我覺得還是放在StackOverFlow上問更合適。兩者並不同步。硬體Vsync被轉化為兩個不同delay的軟體信號,分別供給Choreographer和SurfaceFlinger(並且前者的延時可能是負值),用以在兩者之間製造出充分的時間窗。當硬體產生Vsync後,經過Delay1,開始所有當前窗口的繪製;經過Delay2,SurfaceFlinger服務開始合併所有窗口(Layer)的最新內容,將結果提交給fb來刷新顯示。所以如果某應用未能在時間窗內及時完成顯示工作(queueBuffer),它只能等到SurfaceFlinger的下一輪合併了。以上流程依賴具體平台實現,僅供參考。
Vsync是在顯示器讀完顯卡buffer一幀之後,顯卡的垂直同步脈衝檢測器發出的信號。這個信號頻率和顯示器刷新頻率差不多60FPS(View結構高FPS相當於浪費)。分別給Choreographer和SurfaceFlinger發信號使顯示頻率和繪畫頻率及合併頻率一致,因為Choreographer和SurfaceFlinger之間不是直接傳遞,而是用了queueBuffer,所以讓他們兩個之間有個時間窗,但是頻率是一致的,只是先調後調。
是做方案嗎?
推薦閱讀:
※android開發,你們還在findviewbyid嗎?
※Android程序員技術等級標準?
※為什麼Android的Void實現和JDK有區別?
※小米的miui能否解決安卓的SD卡文件夾「碎片化」?
※你遇到過哪些代碼優雅的安卓項目?