SurfaceView 與view區別詳解

SurfaceView 與view區別詳解

 

https://blog.csdn.net/u011339364/article/details/83347109html

 

在Android系統中,有一種特殊的視圖,稱爲SurfaceView,它擁有獨立的繪圖表面,即它不與其宿主窗口共享同一個繪圖表面,因爲擁有獨立的繪圖表面,所以SurfaceView的UI就能夠在一個獨立的線程中進行行繪製,因爲不佔用主線程資源,SurfaceView一方面能夠實現複雜而高效的UI。android

SurfaceView的繪製方式效率很是高,由於SurfaceView的窗口刷新的時候不須要重繪應用程序的窗口(android普通窗口的視圖繪製機制是一層一層的,任何一個子元素或者是局部的刷新都會致使整個視圖結構所有重繪一次,所以效率很是低下)。
View經過刷新來重繪視圖,Android系統經過發出VSYNC信號來進行屏幕的重繪,刷新的時間間隔通常爲16ms,在一些須要頻繁刷新的界面,若是刷新執行不少邏輯繪製操做,就會致使刷新使用時間超過了16ms,就會致使丟幀或者卡頓,好比你更新畫面的時間過長,那麼你的主UI線程會被你的繪製函數阻塞,那麼將沒法響應按鍵,觸屏等消息,會形成 ANR 問題,SurfaceView雖然繼承自View,但擁有獨立的surface,即它不與其宿主窗口共享同一個surface,能夠單獨在一個線程進行繪製,並不會佔用主線程的資源,這樣,繪製就會比較高效,遊戲,視頻播放,直播,均可以用SurfaceView來實現,SurfaceView有兩個子類GLSurfaceView和VideoView
canvas

 

SurfaceView裏面鑲嵌的Surface是在包含SurfaceView的宿主Activity窗口(頂層視圖對應的Surface)後面,用來描述SurfaceView的Layer的Z軸位置是小於用來描述其宿主Activity窗口的Layer的Z軸位置的,這樣SurfaceView的Layer就被擋住看不見了,SurfaceView提供了一個可見區域,只有在這個可見區域內的surface部份內容纔可見,就好像SurfaceView會在宿主Activity窗口上面挖一個「洞」出來,以便它的UI能夠漏出來對用戶可見,實際上,SurfaceView只不過是在其宿主Activity窗口上設置了一塊透明區域緩存

如上Activity窗口的頂層視圖DecorView及其兩個TextView控件都是經過窗口的Surface對應的Canvas繪製在SurfaceFlinger服務中的同一個Layer上,而SurfaceView的UI是經過其本身的Surface對應的Canvas繪製在SurfaceFlinger服務中的另一個Layer上。ide

要了解 SurfaceView ,還須瞭解它的另外兩個組件:Surface 和 SurfaceHolder,他們三者之間的關係實質上就是 MVC,Model就是數據模型的意思也就是這裏的Surface;View即視圖也就是這裏的SurfaceView;SurfaceHolder很明顯能夠理解爲Controller(控制器)。函數

在SurfaceView中你能夠經過SurfaceHolder接口訪問它內部的surface,而咱們執行繪製的方法就是操做這個 Surface內部的Canvas,處理Canvas畫的效果和動畫,大小,像素等,getHolder()方法能夠獲得這個SurfaceHolder,經過SurfaceHolder來控制surface的尺寸和格式,或者修改監視surface的變化等等。動畫

SurfaceHolder有三個回調方法能夠監聽SurfaceView中的surface的生命週期,SurfaceView一開始建立出來後,它擁有的Surface不必定會一塊兒建立出來,SurfaceView變得可見時,surface被建立,SurfaceView隱藏前,surface被銷燬,被建立了表示能夠開始準備繪製了,而被銷燬後咱們要釋放其餘資源,Surfaceview通常會繼承SurfaceHolder的Callback接口,SurfaceHolder.Callback具備以下的方法:
   surfaceCreated(SurfaceHolder holder):當Surface第一次建立後會當即調用該函數,能夠在該函數中作些和繪製界面相關的初始化工做,通常狀況下都是在新線程來繪製界面,因此不要在這個函數中繪製Surface。 
   surfaceChanged(SurfaceHolder holder, int format, int width,int height):當Surface的狀態(大小和格式)發生變化的時候會調用該函數,在surfaceCreated調用後該函數至少會被調用一次。 
   surfaceDestroyed(SurfaceHolder holder):當Surface被摧毀前會調用該函數,該函數被調用後就不能繼續使用Surface了,通常在該函數中來清理使用的資源。 spa

特別須要注意的是SurfaceView和SurfaceHolder.Callback的全部回調方法都是在主線程中回調的,在繪製前必須先合法的獲取 Surface 才能開始繪製內容, SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed() 之間的狀態爲合法的,在這以外使用Surface都會出錯。.net

在使用SurfaceView過程當中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或則Canvas lockCanvas(Rect dirty)函數來鎖定而且獲取Surface中的Canvas畫布對象,經過在Canvas上繪製內容來修改Surface中的數據,若是Surface被別的線程佔有不可編輯或則還沒有建立或者已經被銷燬,調用該函數會返回null。線程

在unlockCanvas() 和 lockCanvas()之間Surface的內容是不緩存的,因此須要徹底重繪Surface的內容,若是爲了提升效率只重繪變化的部分則能夠調用lockCanvas(Rect dirty)函數來指定一個dirty區域,這樣該區域外的內容會緩存起來,只更新須要重繪的區域,相對部份內存要求比較高的遊戲來講,不重畫dirty外的其餘區域的像素,能夠提升速度。

在調用lockCanvas函數獲取Surface的Canvas後,SurfaceView會利用Surface的一個同步鎖鎖住畫布Canvas,直到調用unlockCanvasAndPost(Canvas canvas)函數,才解鎖畫布並提交改變,將圖形顯示,這裏的同步機制保證Surface的Canvas在繪製過程當中不會被改變(被摧毀、修改),避免多個不一樣的線程同時操做同一個Canvas對象。

雙緩衝:SurfaceView在更新視圖時用了兩個Canvas,一張frontCanvas和一張backCanvas,每次實際顯示的是frontCanvas,backCanvas存儲的是上一次更改前的視圖,當使用lockCanvas()獲取畫布時,獲得的其實是backCanvas而不是正在顯示的frontCanvas,當你在獲取到的backCanvas上繪製完成後,再使用unlockCanvasAndPost(canvas)提交backCanvas視圖,那麼這張backCanvas將替換正在顯示的frontCanvas被顯示出來,原來的frontCanvas將切換到後臺做爲backCanvas,這樣作的好處是在繪製期間不會出現黑屏。

SurfaceView類的成員變量mRequestedType描述的是SurfaceView的繪圖表面Surface的類型,通常來講,它的值可能等於SURFACE_TYPE_NORMAL或者SURFACE_TYPE_PUSH_BUFFERS,
當一個SurfaceView的繪圖表面的類型等於SURFACE_TYPE_NORMAL的時候,就表示該SurfaceView的繪圖表面所使用的內存是一塊普通的內存,通常來講,這塊內存是由SurfaceFlinger服務來分配的,咱們能夠在應用程序內部自由地訪問它,便可以在它上面填充任意的UI數據,而後交給SurfaceFlinger服務來合成,而且顯示在屏幕上,在這種狀況下,在SurfaceFlinger服務一端使用一個Layer對象來描述該SurfaceView的繪圖表面。

當一個SurfaceView的繪圖表面的類型等於SURFACE_TYPE_PUSH_BUFFERS的時候,就表示該SurfaceView的繪圖表面所使用的內存不是由SurfaceFlinger服務分配的,咱們不可以在應用程序內部對它進行操做,因此不能調用lockCanvas來獲取Canvas對象進行繪製了,例如當一個SurfaceView是用來顯示攝像頭預覽或者視頻播放的時候,咱們就會將它的繪圖表面的類型設置爲SURFACE_TYPE_PUSH_BUFFERS,這樣攝像頭服務或者視頻播放服務就會爲該SurfaceView繪圖表面建立一塊內存,而且將採集的預覽圖像數據或者視頻幀數據源源不斷地填充到該內存中去,在這種狀況下,在SurfaceFlinger服務一端使用一個LayerBuffer對象來描述該SurfaceView的繪圖表面。因此:決定surfaceView的內存是普通內存(由開發者本身決定用來繪製什麼)仍是專用的內存(顯示攝像頭或者視頻等,開發者沒法使用這塊內存)由mRequestType決定,咱們在建立了一個SurfaceView以後,能夠調用它的SurfaceHolder對象的成員函數setType來修改該SurfaceView的繪圖表面的類型,繪圖表面類型爲SURFACE_TYPE_PUSH_BUFFERS的SurfaceView的UI是不能由應用程序來控制的,而是由專門的服務來控制的,例如,攝像頭服務或者視頻播放服務。SurfaceView類的成員變量mRequestedType目前接收以下的參數:SURFACE_TYPE_NORMAL:用RAM緩存原生數據的普通Surface SURFACE_TYPE_HARDWARE:適用於DMA(Direct memory access )引擎和硬件加速的Surface SURFACE_TYPE_GPU:適用於GPU加速的Surface SURFACE_TYPE_PUSH_BUFFERS:代表該Surface不包含原生數據,Surface用到的數據由其餘對象提供。 

相關文章
相關標籤/搜索