1. 程式人生 > >Android繪製流程一問到底(無程式碼)

Android繪製流程一問到底(無程式碼)

## 前言 週二好呀,今天給大家帶來一點輕鬆簡單的內容,沒有程式碼,請享用~ * 高刷手機,60hz,120hz指的是什麼? * 螢幕的重新整理過程。 * 幀率,VSYNC是什麼? * 單快取,雙快取,三快取。 * 程式碼中修改了UI,螢幕是怎麼進行重新整理的? * 如果介面保持靜止不變,螢幕會重新整理嗎?影象會被重新繪製嗎? ## 高刷手機,60hz,120hz指的是什麼 指的是螢幕的`重新整理頻率`,也就是一秒內螢幕重新整理的次數。重新整理頻率這個引數是手機出廠就決定的,取決於硬體的固定引數。 `高刷手機`,一般就是指高重新整理率螢幕,也就是大於一般的60hz,比如`90hz,120hz`等等。它的特點就在於每秒重新整理的頻率更高,使得畫面更加`流暢,順滑`,就算出現丟幀等情況,畫面還能保證一個穩定性。 ## 螢幕的重新整理過程。 螢幕的重新整理過程是每一行從左到右,從上到下,順序顯示畫素點。當整個螢幕重新整理完畢,即一個垂直重新整理週期完成,會有短暫的空白期,此時發出` VSync` 訊號。如果是60hz的手機,那麼每次螢幕重新整理的過程佔用時間就是`16ms(1000/60)`左右。 一般一個圖形介面的繪製,需要`CPU`準備資料,然後`GP`U進行繪製,繪製完寫入`快取區`,然後螢幕按照重新整理頻率來從這個快取區中取圖形顯示。 所以整個重新整理過程是`CPU,GPU,螢幕(Display)`三方合作的工作關係。 ## 幀率,VSYNC是什麼 `幀率`,就是`GPU`一秒內繪製操作的幀數,單位是`fps`。遊戲中比較常見,越大也就代表越流暢。所以這個引數並不是固定值,但是如果螢幕重新整理頻率是`60hz`,你的幀率大於`60fps`也就浪費了,所以一般情況下最好是幀率和螢幕重新整理頻率保持一致,即同樣是`60fps`。這樣就能保證一個比較平滑的視覺動畫。 `VSync`,垂直同步,在`Android4.1`引進,是一種定時傳送繪製訊號的機制,它的作用就是讓幀率和螢幕重新整理率保持一致,防止跳幀卡頓等等。玩過lol的朋友應該都知道,設定介面就可以開啟垂直同步選項。 正常如果沒有開啟`vsync`,螢幕重新整理有可能會出現什麼問題呢? ![](https://images2018.cnblogs.com/blog/1453367/201807/1453367-20180730101348847-1849622410.png) 如圖,由於`CPU,GPU`繪製圖像的時間不定,所以就有可能會發生卡頓情況,也就是下一幀的資料沒準備好,無法正常顯示到螢幕上。 如果我們開啟`vsync`,也就是給`CPU和GPU`規定了開始繪製幀資料的時間。開啟後,系統會每16ms就傳送一次vsync訊號,`CPU`收到訊號就開始處理資料,然後GPU繪製圖像。這樣就把16ms最大化的利用起來了,只要`CPU和GPU`在`16ms`之內把下一幀資料處理好,那麼螢幕就能從快取區中拿到下一幀資料並顯示出來了。如圖: ![](https://images2018.cnblogs.com/blog/1453367/201807/1453367-20180730101448858-533922394.png) 所以`vsync`訊號就是為了保證`16ms`繪製出一幀的資料出來。使得螢幕每16ms重新整理一次,就能用到最新的幀資料了。這樣畫面就是比較流暢的了。 這整個過程其實就在`Choreographer`類中實現的,包括同步屏障的使用也在其中,下次會具體講到。 ## 單快取,雙快取,三快取 * `單快取`。就是`CPU`計算好的資料交給`GPU`,然後`GPU`進行影象繪製,最後放到`快取區`。而螢幕就直接從這個`快取區`中拿到資料並顯示。 但是這樣做有個問題就是,因為`Display和GPU`都是操作的同一個快取,就會出現同一個畫面中有不同幀的資料。比如螢幕重新整理的時候,第二幀還沒繪製完,那麼快取中就有第二幀資料還有第一幀殘留的資料,這樣顯示出來的畫面就有`兩個幀`的畫面了,比如`畫面撕裂`。 * `雙快取`。這個雙快取就是設計出來解決單快取問題的。既然`Display和GPU`不能共用一個快取,那麼就設計兩個快取就可以啦。 `FrameBuffer`來做顯示輸出,也就是螢幕每次從這個快取中取圖形資料。`BackBuffer`用來放下一幀的畫面,也就是CPU每次繪製資料到這個快取中。然後當CPU完整繪製完下一幀圖形,也就是`BackBuffer`準備好,螢幕也顯示完上一幀資料的時候,就進行快取交換,把資料同步到`FrameBuffer`。而這個快取交換點,就是vsync訊號時刻。 * `三快取`。 Android4.1 引入,一般來說,雙快取就能夠使用了,但是為什麼還有一個三快取呢?看圖: ![](https://images2018.cnblogs.com/blog/1453367/201807/1453367-20180730101511348-1184063168.png) 雙快取情況下,如果`CPU/GPU`處理資料過慢,就會發生上圖的情況。也就是`vsync`訊號來的時候,上一幀資料還沒繪製完,於是A資料圖片顯示了兩幀的時間,而且由於vsync來的時候cpu才開始處理資料,而圖上`vsync`來的時候,`GPU`還在處理資料,導致`GPU`處理完了之後,無法觸發下一幀資料的處理,浪費了一大半時間。後面情況類似,只要`CPU/GPU`處理資料過慢,就會發生Jank(卡頓等問題) 所以這時候就引入了第三個快取,如圖: ![](https://images2018.cnblogs.com/blog/1453367/201807/1453367-20180730101511348-1184063168.png) 如圖所示,在`vsync`來的時候,如果`GPU`還沒處理好資料幀B的圖形,這時候第三個快取區可以來處理後面C幀的資料,然後第二個vsync訊號來的時候,雖然第三快取區還在用作處理C幀資料,但是之前的`BackBuffer`又可以來快取下一幀的資料了。 這樣一來,雖然A幀資料還是顯示了兩個時間點,但是後面由於有`新Buffer`的加入,可以保證後續影象顯示能正常平滑的顯示了。就相當於多了一個勞動力,可以最大限度利用好時間。 ## 程式碼中修改了UI,螢幕是怎麼進行重新整理的? 當我們用程式碼修改了UI,比如使用了`setText`,修改`Textview`的值。這時候螢幕不會馬上繪製重新整理。而是會呼叫到`invalidate`方法請求重繪,然後會向`VSYNC服務`傳送請求,等到下一個`VSYNC`訊號觸發的時候,就開始上面說過的流程,也就是處理資料,繪製圖像,具體所做的工作就是`測量—佈局—繪製`。接著,螢幕就可以拿到快取區中繪製好的影象並顯示到螢幕上了。 所以任何UI的改變,都要遵從上述所說的`VSYNC`機制,只是這個過程很短。當然為了保證最快時間繪製到螢幕上,而不讓其他訊息影響到`VSYNC`的響應速度,就加入了同步屏障。 ## 如果介面保持靜止不變,螢幕會重新整理嗎?影象會被重新繪製嗎? 首先,螢幕重新整理頻率這個是`不會變`的,也就是每隔`16ms`左右就會進行一次重新整理,而重新整理的幀資料就是我們的程式內部在接收到重新整理的`vsync`訊號之後,經過計算繪製後的影象資料。 但是,app並不是每一個`vsync`訊號都能接收到的,只有當應用有繪製需求的時候,才會通過`scheduledVsync` 方法申請`VSYNC`訊號,然後下一個螢幕重新整理的訊號才能被我們的程式所接收到,也就是`Choreographer`類的`onVsync`方法才能被執行,然後就開始`測量—佈局—繪製`等工作了。 所以,如果介面不變化,我們的程式就收不到`VSYNC訊號`,也就無法處理資料進行繪製了。只有當需要改變介面的時候,才會去申請這個螢幕重新整理服務,才能接收到`VSYNC訊號`。這種情況下,螢幕還會進行重新整理,只不過重新整理的都是`同樣`的影象資料。 ## 參考 https://juejin.cn/post/6863756420380196877 https://www.cnblogs.com/frrj/archive/2018/07/30/brief-info-of-android-display.html https://www.jianshu.com/p/10db590ed9a6 ## 拜拜 > 有一起學習的小夥伴可以關注下❤️ 我的公眾號——碼上積木,每天剖析一個知識點,我們一起積累知識。公眾號回覆111可獲得面試題《思考與解答》以往期刊。 ![](https://s1.ax1x.com/2020/11/05/BRV7Mq