淺談移動端 View 的顯示過程

做者:個推安卓開發工程師 一七編程

隨着科技的發展,各類移動端早已成爲人們平常生活中不可或缺的部分,人們使用移動端產品工做、社交、娛樂……移動端界面的流暢性已經成爲影響用戶體驗的重要因素之一。那麼你是否思考過移動端所展示的流暢畫面是如何實現的呢?緩存

本文經過對移動端View顯示過程的簡略分析,幫助開發者瞭解View渲染的邏輯,更好地優化本身的APP。性能優化

上圖展現的是一個完整的頁面渲染過程。經過上圖,咱們能夠初步瞭解每一幀頁面從代碼佈局的編寫到展現給使用者,其背後的邏輯是如何一步一步執行的。架構


屏幕如何呈像

像素點

在電子屏幕中顯示的圖片,其實都是由一個個「小點」所組成的,這些「小點」被稱爲「像素點」。每個像素點都有本身的顏色,每一張完整的圖片都是由它們相連拼接造成的。併發

每一個像素點通常都有 3 個子像素:紅、綠、藍,根據這三種原色,咱們可以調製出各類各樣的顏色。佈局

 

大電視機

與如今的平板電視不一樣的是,之前的黑白電視機或者大背投彩電,老是帶着大大的「後背」。「大後背」電視其實就是陰極射線管電視機,俗稱顯像管電視。其成像原理是電子槍發射出的電子束(陰極射線)經過聚焦系統和偏轉系統,射向屏幕上塗有熒光層的指定位置。被電子束轟擊的每一個位置,熒光層都會產生一個小亮點,最終小亮點們將會組成一幅幅影像,顯示在電視屏幕上。性能

這也是之前大電視機的屏幕都呈圓弧形的緣由。由於越接近圓形,邊長到中心的距離越相近,呈像越均勻。那爲何當磁鐵貼近電視機時,會讓電視機的成像出現問題呢?那是由於磁鐵會干擾電子束的正常軌跡,而且在貼近屏幕的時候,也可能使得屏幕的熒光層磁化,出現一個個不正常的光斑。優化

下圖展現的是攝像機慢放後,電子束的繪製過程。ui

 

LCD 和 OLED

隨着科技的不斷進步,電視、手機、電腦的體積愈來愈薄,射線管顯像方式也逐漸被淘汰。目前在手機市場上佔據主流地位的是 LCD 和 OLED 兩種屏幕。線程

LCD 全稱爲 Liquid Crystal Display ,即液晶顯示器。OLED 全稱爲 Organic Light-Emitting Diode ,即有機發光二極管。這二者之間存在顯著的差異:

1. 二者成像原理不一樣:LCD 是靠白色的背光穿透彩色薄膜顯色的,而 OLED 則是靠每一個像素點自行發光。

2. 在耗電量方面:LCD的耗電量較高,即便只顯示一個亮點,LCD 的背光源也須要一直髮光,並且容易出現漏光現象。而OLED的每一個像素都能獨立工做,並且 能夠自行發光,所以採用OLED的設備能夠製做得更薄,甚至能夠彎曲。

3.在製做方面: LCD使用的是無機材料, OLED 則須要使用有機材料,所以 OLED的製做費用更高,而且使用壽命不如 LCD 。


圖形顯示核心 GPU

與CPU相對比,GPU的計算單元更多,更擅長大規模併發計算,例如密碼破解、圖像處理等。CPU 則是遵循馮諾依曼架構存儲程序順序執行,在大規模並行計算能力上,受到的限制更大,所以更擅長邏輯控制。


應用程序編程接口 API (OpenGL)

在沒有統一的 API 以前,開發者須要在各式各樣的圖形硬件上編寫各類自定義接口和驅動程序,工做量極大。

1990 年 SGI(硅谷圖形公司)成爲了工做站 3D 圖形領域的領導者,並將其 API 轉變爲一項開放標準,即 OpenGL。後來,SGI還促成了 OpenGL 架構審查委員會(OpenGL ARB)的建立。

 

垂直同步 Vertical Synchronization

當咱們在使用手機 APP 的過程當中,發現頁面出現卡頓現象,那麼極有多是頁面沒有在 16ms 內更新致使的。實際上,人眼與大腦之間的協做沒法感知超過 60fps 的畫面更新。60fps 至關因而每秒 60 幀,那麼每一個頁面須要在 1000/60 = 16ms 內更新爲其餘頁面,纔不會讓咱們感覺到頁面的卡頓。

而在沒有 VSync 的狀況下可能會出現如下狀況:


如上圖所示,在沒有 VSync 的狀況下,會出現須要顯示第二幀時,其還沒有處理完成的狀況,所以Display 中顯示的還是第一幀。這會形成該幀顯示時長超過16ms,從而致使頁面卡頓的現象。

爲了使 CPU、GPU 生成幀的速度與 Display 保持一致,Android 系統每 16ms 就會發出一次 VSYNC 信號,觸發 UI 渲染更新。

從上圖中咱們能夠看出,每隔 16ms ,安卓會發出一個 VSync 信號,收到信號後 CPU 開始處理下一幀的的內容,GPU 在 CPU 處理結束以後,將會進行光柵化,此時屏幕上顯示的是上一幀已經處理完成的頁面。如此反覆,就能夠在頁面中展現一幅幅的指定畫面。而確保畫面流暢的前提是CPU 和 GPU 處理一幀所花費的時間不能超過 16 ms,不然就會出現如下狀況:

當CPU 和 GPU 處理一幀的時間超過了16 ms時,在第一個 Display 中,因爲 GPU 處理 B 畫面的時間過長,致使系統發出 VSync 信號時, Display不能及時地顯示出 B 畫面,而重複顯示A頁面,形成卡頓。

此外,在第二個 Display 中,因爲 A Buffer 還在被 Display 所使用,不能在收到 VSync 信號後開始處理下一幀的頁面,致使該時間段內 CPU 的閒置。爲了不這種時間的浪費,三緩存機制由此出現:

如上圖所示,在三緩存機制中,當 A 緩存被 Display 使用、B 緩存被 GPU 處理時,系統會發出 Vsync 信號,並加入新的緩存 C ,用來緩存下一幀的內容。這種方式雖然不能徹底避免 A頁面的重複顯示,可是可以讓後面頁面的顯示更加平滑。


View 的繪製流程

View 的繪製是從 ViewRootImpl 的 performTraversals() 方法開始的,其總體流程大體分爲三步,以下圖所示:

measure

控件測量過程從 performMeasure() 方法開始。在該方法中childWidthMeasureSpec 和 childHeightMeasureSpec,分別是用來肯定寬度和高度的。

MeasureSpec 是一個 int 值,它存儲着兩個信息:低 30 位是 View 的 specSize,高 2 位是 View 的 specMode。

specMode 有三種類型:

1.UNSPECIFIED

父視圖對子視圖沒有任何限制,能夠將視圖按照開發者的意願設置成任意的大小,在通常開發過程當中不會用到。

2.EXACTLY

父視圖爲子視圖指定一個確切的尺寸,該尺寸由 specSize 的值來決定。

3.AT_MOST

父視圖爲子視圖指定一個最大的尺寸,該尺寸的最大值是 specSize。

觀察 View 的 measure() 方法,能夠發現該方法是被 final 修飾的,所以 View 的子類只可以經過重載 onMeasure() 方法來完成本身的測量邏輯。

在 onMeasure() 方法中:

調用 getDefaultSize() 方法來獲取視圖的大小:

該方法中的第二個參數 measureSpec 是從 measure() 方法中傳遞過來的:經過 getMode() 和 getSize() 解析獲取其中對應的值,再根據 specMode 給最終的 size 賦值。

不過以上只是一個簡單控件的一次 measure 過程,在真正測量的過程當中,因爲一個頁面每每包含多個子 View ,因此須要循環遍歷測量,在 ViewGroup 中有一個 measureChildren() 方法,就是用來測量子視圖的:

measure 總體流程的方法調用鏈以下:

layout

在performTraversals() 方法的測量過程結束後,進入 layout 佈局過程:

performLayout(lp,desiredWindowWidth,desiredWindowHeight);

該過程的主要做用即根據子視圖的大小以及佈局參數,將相應的 View 放到合適的位置上。

host.layout(0,0,host.getMeasuredWidth(),host.getMeasuredHeight());

如上,layout() 方法接收了四個參數,按照順時針,分別是左上右下。該座標針對的是父視圖,以左上爲起始點,傳入了以前測量出的寬度和高度。以後,讓咱們進入到 layout() 方法中觀察:

咱們經過 setFrame() 方法給四個變量賦值,判斷 View 的位置是否變化以及是否須要從新進行 layout,並且其中還調用了 onLayout() 方法。

在進入該方法後,咱們能夠發現裏面是空的,這是由於子視圖的具體位置是相對於父視圖而言的,因此 View 的 onLayout 爲空實現。

再進入 ViewGroup 類中查看,咱們能夠發現,這實際上是一個抽象的方法,在這樣的狀況下, ViewGroup 的子類便須要重寫該方法:

draw

繪製的流程主要以下圖所示,該流程也是存在遍歷子 View 繪製的過程:

 

須要注意的是,View 的 onDraw() 方法是空的,這是由於每一個視圖的內容都不相同,這個部分交由子類根據自身的須要來處理,才更加合理:


安卓渲染機制的總體流程

 

1. APP 在 UI 線程構建 OpenGL 渲染須要的命令及數據;

2. CPU 將數據上傳(共享或者拷貝)給 GPU 。(PC 上通常有顯存,可是 ARM 這種嵌入式設備內存通常是 GPU 、 CPU 共享內存);

3. 通知 GPU 渲染。通常而言,真機不會阻塞等待 GPU 渲染結束,通知結束後就返回執行其餘任務;

4. 通知 SurfaceFlinger 圖層合成;

5. SurfaceFlinger 開始合成圖層。


總結

移動端技術發展很快,而畫面顯示優化是一個持續發展的實踐課題,貫穿於每一個開發者的平常工做中。將來,個推技術團隊將繼續關注移動端的性能優化,爲你們分享相關的技術乾貨。

相關文章
相關標籤/搜索