日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

說說Android的UI刷新機制的實現

瀏覽:133日期:2022-09-26 18:59:38

本文主要解決以下幾個問題:

我們都知道Android的刷新頻率是60幀/秒,這是不是意味著每隔16ms就會調用一次onDraw方法? 如果界面不需要重繪,那么16ms到后還會刷新屏幕嗎? 我們調用invalidate()之后會馬上進行屏幕刷新嗎? 我們說丟幀是因為主線程做了耗時操作,為什么主線程做了耗時操作就會引起丟幀? 如果在屏幕快要刷新的時候才去OnDraw()繪制,會丟幀嗎?

好了,帶著以上問題,我們進入源碼來找尋答案。

一、屏幕繪制流程

屏幕繪制機制的基本原理可以概括如下:

說說Android的UI刷新機制的實現

整個屏幕繪制的基本流程是:

應用向系統服務申請buffer 系統服務返回buffer 應用繪制后提交buffer給系統服務

如果放到Android中來,那么就是:

說說Android的UI刷新機制的實現

在Android中,一塊Surface對應一塊內存,當內存申請成功后,App端才有繪圖的地方。由于Android的view繪制不是今天的重點,所以這里點到為止~

二、屏幕刷新分析

屏幕刷新的時機是當Vsync信號到來的時候,具體如圖:

說說Android的UI刷新機制的實現

在Android端,是誰在控制 Vsync 的產生?又是誰來通知我們應用進行刷新的呢? 在Android中, Vysnc 信號的產生是由底層 HWComposer 負責的,而通知應用進行刷新,是Java層的 Choreographer ,Android整個屏幕刷新的核心就在于這個 Choreographer 。

下面我們結合代碼一起來看一下。

每次當我們要進行ui重繪的時候,都會調用 requestLayout() ,所以,我們從這個方法入手:

2.1 requestLayout()

----》類名:ViewRootImpl @Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; //重點 scheduleTraversals(); } }

2.2 scheduleTraversals()

----》類名:ViewRootImpl void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ...... } }

可以看到,在這里并沒有立即進行重繪,而是做了兩件事情:

往消息隊列里面插入一條SyncBarrier(同步屏障) 通過Cherographer post了一個callback

接下來,我們簡單說一下這個 SyncBarrier (同步屏障)。

異步屏障的作用在于:

阻止同步消息的執行 優先執行異步消息

為什么要設計這個 SyncBarrier 呢?主要原因在于,在Android中,有些消息是十分緊急的,需要馬上執行,如果說消息隊列里面普通消息太多的話,那等到執行它的時候可能早就過了時機了。

到這里,可能有人會跟我一樣,覺得為什么不干脆在Message里搞個優先級,按照優先級來進行排序呢?弄個 PriorityQueue 不就完了嗎?

我自己的理解是,在Android中,消息隊列的設計是一個 單鏈表 ,整個鏈表的排序是根據時間進行排序的,如果此時再加入一個優先級的排序規則,一方面會復雜會排序規則,另一方面,也會使得消息不可控。因為優先級是可以用戶自己在外面填的,那樣不就亂套了嗎?如果用戶每次總填最高的優先級,這樣就會導致系統消息很久才會消費,整個系統運作就會出問題,最后影響用戶體驗,所以,我自己覺得Android的同步屏障這個設計還是挺巧妙的~

好了,總結一下,執行 scheduleTraversals() 后,會插入一個屏障,保證異步消息的優先執行。

插入一個小小的思考題: 如果說我們在一個方法里連續調用了 requestLayout() 多次,那么請問:系統會插入多條屏障或者 post 多個 Callback 嗎? 答案是不會,為什么呢?看到 mTraversalScheduled 這個變量了嗎?它就是答案~

2.3 Choreographer.postCallback()

先來簡單說一下 Choreographer , Choreographer 中文翻譯叫 編舞者 ,它的主要作用是進行系統協調的。(大家可以上網google下實際工作中的編舞者,這個類名真的起的很貼切了~)

Choreographer 這個類是應用怎么初始化的呢?是通過 getInstance() 方法:

public static Choreographer getInstance() { return sThreadInstance.get(); } // Thread local storage for the choreographer. private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() { 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); if (looper == Looper.getMainLooper()) {mMainInstance = choreographer; } return choreographer; } };

這里貼出來是為了提醒大家, Choreographer 不是單例,而是每個線程都有單獨的一份。

好了,回到我們的代碼:

----》類名:Choreographer //1 public void postCallback(int callbackType, Runnable action, Object token) { postCallbackDelayed(callbackType, action, token, 0); } //2 public void postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis) { .... postCallbackDelayedInternal(callbackType, action, token, delayMillis); } //3 private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {...mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);if (dueTime <= now) {scheduleFrameLocked(now); } else {... } }

Choreographer post的callback會放入 CallbackQueue 里面,這個 CallbackQueue 是一個單鏈表。

首先會根據callbackType得到一條 CallbackQueue 單鏈表,之后會根據時間順序,將這個callback插入到單鏈表中;

2.4 scheduleFrameLocked()

----》類名:Choreographer private void scheduleFrameLocked(long now) { ... // 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 {... } } }

scheduleFrameLocked 的作用是:

如果當前線程就是 Cherographer 的工作線程的話,那么就直接執行 scheduleVysnLocked 否則,就發送一個異步消息到消息隊列里面去 ,這個異步消息是不受同步屏障影響的,而且這個消息還要插入到消息隊列的頭部,可見這個消息是非常緊急的

跟蹤源代碼,我們發現,其實 MSG_DO_SCHEDULE_VSYNC 這條消息,最終執行的也是 scheduleFrameLocked 這個方法,所以我們直接跟蹤 scheduleVsyncLocked() 這個方法。

2.5 scheduleVsyncLocked()

----》類名:Choreographer 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 { //mReceiverPtr是Native層一個類的指針地址 //這里這個類指的是底層NativeDisplayEventReceiver這個類 //nativeScheduleVsync底層會調用到requestNextVsync()去請求下一個Vsync, //具體不跟蹤了,native層代碼更長,還涉及到各種描述符監聽以及跨進程數據傳輸 nativeScheduleVsync(mReceiverPtr); } }

這里我們可以看到一個新的類: DisplayEventReceiver ,這個類的作用是注冊Vsync信號的監聽,當下個Vsync信號到來的時候就會通知到這個 DisplayEventReceiver 了。

在哪里通知呢?源碼里注釋寫的非常清楚了:

----》類名:DisplayEventReceiver // Called from native code. <---注釋還是很良心的 private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) { onVsync(timestampNanos, builtInDisplayId, frame); }

當下一個Vysnc信號到來的時候,會最終調用 onVsync 方法:

public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { }

點進去一看,是個空實現,回到類定義,原來是個抽象類,它的實現類是: FrameDisplayEventReceiver ,定義在 Cherographer 里面:

----》類名:Choreographer private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { .... }

2.6 FrameDisplayEventReceiver.onVysnc()

----》類名:Choreographer private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { @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() { .... doFrame(mTimestampNanos, mFrame); } }

onVsync 方法往 Cherographer 所在線程的消息隊列中發送的一個消息,這個消息是就是它自己(它實現了Runnable),所以最終會調用到 doFrame() 方法。

2.7 doFrame(mTimestampNanos, mFrame)

doFrame()的處理分為兩個階段:

void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { //1、階段一 long intendedFrameTimeNanos = frameTimeNanos; startNanos = System.nanoTime(); final long jitterNanos = startNanos - frameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) {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.');}... } ... }

frameTimeNanos 是當前的時間戳,將當前的時間和開始時間相減,得到這一幀處理花費了多長,如果大于 mFrameIntervalNano ,說明處理耗時了,之后就打印出我們日常見到的 The application may be doing too much work on its main thread 。

階段二:

void doFrame(long frameTimeNanos, int frame) { ...try {//階段2 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); } ... }

doFrame() 的第二個階段做的是處理各種callback,從CallbackQueue里面取出到執行時間的callback進行處理,那這個callback是怎么樣呢?

這里要回憶一下之前的 postCallback() 操作:

說說Android的UI刷新機制的實現

這個 Callback 其實就一個 mTraversalRunnable ,它是一個 Runnable ,最終會調用到 run() 方法,實現界面的真正刷新:

----》類名:ViewRootImpl final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } void doTraversal() { if (mTraversalScheduled) { ... performTraversals(); ... } } private void performTraversals() { ... //開始真正的界面繪制 performDraw(); ... }

三、總結

經過漫長的代碼跟蹤,整個界面刷新流程算是跟蹤完了,下面我們來總結一下:

說說Android的UI刷新機制的實現

四、問題解答

我們都知道Android的刷新頻率是60幀/秒,這是不是意味著每隔16ms就會調用一次onDraw方法?

這里60幀/秒是屏幕刷新頻率,但是是否會調用onDraw()方法要看應用是否調用requestLayout()進行注冊監聽。

如果界面不需要重繪,那么還16ms到后還會刷新屏幕嗎?

如果不需要重繪,那么應用就不會受到Vsync信號,但是還是會進行刷新,只不過繪制的數據不變而已;

我們調用invalidate()之后會馬上進行屏幕刷新嗎?

不會,到等到下一個Vsync信號到來

我們說丟幀是因為主線程做了耗時操作,為什么主線程做了耗時操作就會引起丟幀

原因是,如果在主線程做了耗時操作,就會影響下一幀的繪制,導致界面無法在這個Vsync時間進行刷新,導致丟幀了。

如果在屏幕快要刷新的時候才去OnDraw()繪制,會丟幀嗎?

這個沒有太大關系,因為Vsync信號是周期的,我們什么時候發起onDraw()不會影響界面刷新;

五、參考文檔

gityuan大神的 Cherographer原理慕課視頻

到此這篇關于說說Android的UI刷新機制的實現的文章就介紹到這了,更多相關Android UI刷新機制內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Android
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
久久国际精品| 亚洲性图久久| 99精品在线观看| 麻豆一区在线| 久久午夜视频| 不卡中文字幕| 欧洲一级精品| 神马午夜久久| 日韩欧美一区二区三区免费观看| 国产视频一区二| 涩涩涩久久久成人精品| 国产日产高清欧美一区二区三区| 青草国产精品| 日本一区二区高清不卡| 久久精品国产久精国产爱| 四虎8848精品成人免费网站| 国产精品美女午夜爽爽| 日本精品另类| 国产精品久久久久77777丨| 综合日韩在线| 欧美~级网站不卡| 久久久久亚洲| 日韩午夜在线| 婷婷久久免费视频| 亚洲日本久久| 国产精品亚洲综合久久| 国产精品99久久免费观看| 免费在线亚洲欧美| 日韩啪啪电影网| 国产日韩欧美一区在线| 欧美激情亚洲| 久久久国产精品一区二区中文| 性欧美videohd高精| 婷婷精品视频| 成人av二区| 日本欧美一区二区| 久久av网址| 免费不卡中文字幕在线| 蜜臀av国产精品久久久久| 久久激情五月婷婷| 日韩在线第七页| 亚洲精品少妇| 日韩成人亚洲| 日韩一区二区三区在线看| 国产欧美69| 欧美日韩在线网站| 久久精品超碰| 不卡在线一区二区| 国产精品黄色片| 亚洲福利国产| 国产精品久久久久久久久久久久久久久 | 国产精品久久久久久久久久妞妞| 麻豆91小视频| 亚洲精品自拍| 国产高潮在线| 日韩在线观看一区二区| 韩国女主播一区二区三区| 丝袜a∨在线一区二区三区不卡| 精品国产精品久久一区免费式| japanese国产精品| 国产suv精品一区| 亚洲欧美久久精品| 久久国产直播| av在线最新| 国产黄色一区| 日本一区二区三区中文字幕| 亚洲婷婷免费| 日韩中文在线电影| 麻豆视频一区二区| 日韩av中文字幕一区二区 | 97精品一区| 国产精品三级| 欧美在线黄色| 日本精品一区二区三区在线观看视频| 欧美日韩一二三四| 秋霞影院一区二区三区| 国内自拍视频一区二区三区| 99tv成人| 特黄特色欧美大片| 日韩精品欧美| 欧美亚洲国产精品久久| 日韩黄色大片| 日韩欧美国产精品综合嫩v| 日韩精品一区二区三区中文 | 久久久久中文| 久久久国产精品一区二区中文| 精品国产精品久久一区免费式| 久久免费福利| 成人在线免费观看91| 麻豆91在线播放| 超碰在线99| 久久蜜桃精品| 午夜在线播放视频欧美| 免费在线成人网| 国产亚洲久久| 亚洲综合在线电影| 亚洲成人精品| 亚洲婷婷丁香| 久久91视频| 女人av一区| 日韩欧美高清一区二区三区| 国产精品亚洲综合色区韩国| 日本午夜精品久久久久| 福利一区二区| 丝瓜av网站精品一区二区| 国产亚洲字幕| 日本免费久久| 亚洲欧美日本国产专区一区| 欧美亚洲一级| 99久久亚洲精品| 日韩1区2区3区| 精品福利久久久| 亚洲精品va| 国产精品极品在线观看| 热久久久久久久| 黑丝一区二区三区| 电影亚洲精品噜噜在线观看| 蜜桃av一区二区三区电影| 高清一区二区| 日本vs亚洲vs韩国一区三区二区| 国产传媒av在线| 日本少妇一区二区| 最新国产拍偷乱拍精品| 精品视频一区二区三区在线观看 | 日韩中文在线播放| 蜜桃av一区| 天堂av在线| 久久亚州av| 黑人精品一区| 日韩中文字幕高清在线观看| 欧美久久天堂| 99国产精品免费视频观看| 欧美成人a交片免费看| 亚洲www免费| 亚洲欧美久久久| 日本午夜精品视频在线观看| 国产精品久久久久久av公交车| 日韩av不卡一区二区| 久久精品国产久精国产| 久久精品99国产国产精| 91九色综合| 日韩国产精品久久久久久亚洲| 亚洲欧美日韩精品一区二区| 久久亚洲专区| 日本亚洲欧美天堂免费| 国产精品自拍区| 国产一区二区三区免费在线| 亚洲精选91| 亚洲精品九九| 亚洲2区在线| 欧美日韩中出| 国产精品久久久久久模特| 国产精品一区二区精品| 国产精品一区二区三区www| 日本免费新一区视频| 日韩av三区| 男女男精品网站| 欧美精品影院| 欧美日韩国产高清| 国产激情久久| 午夜精品网站| 国产极品模特精品一二| 午夜精品网站| 国产精品久久久久久久久免费高清 | 国产一区二区三区四区大秀| 亚洲欧美日韩国产一区二区| 国产高清日韩| 免费久久精品视频| 香蕉精品久久| 精品日韩在线| 免费看精品久久片| 精品捆绑调教一区二区三区| 欧美日韩va| 亚洲精品综合| 伊人久久亚洲影院| 久久久夜夜夜| 婷婷综合六月| 福利在线免费视频| 久久精品国产一区二区| 91麻豆精品激情在线观看最新| 免费国产自线拍一欧美视频| 亚洲91精品| 另类中文字幕国产精品| 丁香婷婷久久| 精品一级视频| 国产精品日本一区二区不卡视频| 婷婷精品在线| 国产欧美日韩在线一区二区| 欧美三区不卡| 国产乱码精品一区二区三区四区 | 美女毛片一区二区三区四区 | 视频一区日韩| 日韩精品亚洲专区在线观看| 亚洲精品少妇| 欧美专区一区| 国产精品www994| 欧美xxxx中国| 亚洲视频综合| 日本 国产 欧美色综合| 亚洲精品在线国产|