SurfaceView技術點總結及源碼解析

我們知道俺祠,當在view繪制時進行耗時操作或者復雜的動畫潘懊,會出現(xiàn)丟幀或卡頓現(xiàn)象线婚,用戶體驗極為不好。Android系統(tǒng)每隔16ms就會發(fā)出一次VSYNC信號觸發(fā)對UI進行渲染,如果這16ms內(nèi)我們沒有完成對視圖的繪制弦蹂,那么就會出現(xiàn)丟幀的情況肩碟。之所以這樣是因為,人眼與大腦之間的協(xié)作無法感知超過60fps的畫面更新凸椿。60幀每秒就意味著:16ms=1000/60Hz削祈。Android提供了SurfaceView來解決這種情況。SurfaceView可以實現(xiàn)復雜的2D動畫脑漫、播放視頻髓抑、攝像頭預覽等。

初識SurfaceView

五年前优幸,就已經(jīng)了解SurfaceView吨拍,知道它可以配合MediaPlayer播放一個視頻流媒體,其中SurfaceHolder是SurfaceView裝載需要顯示的一幀幀圖像的容器网杆。

大部分軟件是如何解析一段視頻流呢羹饰?

首先它需要先確定視頻的格式,這個和解碼相關跛璧,不同的格式視頻編碼不同严里。知道了視頻的編碼格式后,再通過編碼格式進行解碼追城,最后得到一幀一幀的圖像,并把這些圖像快速的顯示在界面上燥撞,即為播放一段視頻座柱。SurfaceView在Android中可以完成這個功能。

SurfaceView雙緩沖機制

SurfaceView跟大部分視頻應用一樣物舒,把視頻流解析成一幀幀的圖像然后顯示色洞。如果把視頻解析過程放到一個線程中完成,可能在上一幀圖像已經(jīng)顯示過后冠胯,下一幀圖像還沒有來得及解析火诸,這樣會導致畫面的不流暢或者聲音和視頻不同步的問題。所以SurfaceView和大部分視頻應用一樣荠察,通過雙緩沖的機制來顯示幀圖像置蜀。那么什么是雙緩沖呢?雙緩沖可以理解為有兩個線程輪番去解析視頻流的幀圖像悉盆,當一個線程解析完幀圖像后盯荤,把圖像渲染到界面中,同時另一線程開始解析下一幀圖像焕盟,使得兩個線程輪番配合去解析視頻流秋秤,以達到流暢播放的效果。

下圖演示了雙緩沖的過程,線程1和線程2配合解析渲染視頻流的幀圖像:

image

SurfaceHolder

SurfaceView內(nèi)部實現(xiàn)了雙緩沖的機制灼卢,但是實現(xiàn)這個功能是非常消耗系統(tǒng)內(nèi)存的绍哎。因為移動設備的局限性,Android在設計的時候規(guī)定鞋真,SurfaceView如果為用戶可見的時候蛇摸,創(chuàng)建SurfaceView的SurfaceHolder用于顯示視頻流解析的幀圖片,如果發(fā)現(xiàn)SurfaceView變?yōu)橛脩舨豢梢姷臅r候灿巧,則立即銷毀SurfaceView的SurfaceHolder赶袄,以達到節(jié)約系統(tǒng)資源的目的。

如果開發(fā)人員不對SurfaceHolder進行維護抠藕,會出現(xiàn)最小化程序后饿肺,再打開應用的時候,視頻的聲音在繼續(xù)播放盾似,但是不顯示畫面了的情況敬辣,這就是因為當SurfaceView不被用戶可見的時候,之前的SurfaceHolder已經(jīng)被銷毀了零院,再次進入的時候溉跃,界面上的SurfaceHolder已經(jīng)是新的SurfaceHolder了。所以SurfaceHolder需要我們開發(fā)人員去編碼維護告抄,維護SurfaceHolder需要用到它的一個回調(diào)撰茎,SurfaceHolder.Callback(),它需要實現(xiàn)如下三個方法:

  1. void surfaceDestroyed(SurfaceHolder holder)
    當SurfaceHolder被銷毀的時候回調(diào)

2.void surfaceCreated(SurfaceHolder holder)
當SurfaceHolder被創(chuàng)建的時候回調(diào)

3.void surfaceChange(SurfaceHolder holder)
當SurfaceHolder的尺寸發(fā)生變化的時候被回調(diào)

SurfaceView與普通View的區(qū)別

SurfaceView打洼,它擁有獨立的繪圖表面龄糊,即它不與其宿主窗口共享同一個繪圖表面。由于擁有獨立的繪圖表面募疮,因此SurfaceView的UI就可以在一個獨立的線程中進行繪制炫惩。又由于不會占用主線程資源,SurfaceView一方面可以實現(xiàn)復雜而高效的UI阿浓,另一方面又不會導致用戶輸入得不到及時響應他嚷。

普通的Android控件,例如TextView芭毙、Button等筋蓖,都是將自己的UI繪制在宿主窗口的繪圖表面之上,這意味著它們的UI是在應用程序的主線程中進行繪制的稿蹲。由于應用程序的主線程除了要繪制UI之外扭勉,還需要及時地響應用戶輸入,否則的話苛聘,系統(tǒng)就會認為應用程序沒有響應了涂炎,因此就會彈出一個ANR對話框出來忠聚。對于一些游戲畫面,或者攝像頭預覽唱捣、視頻播放來說两蟀,它們的UI都比較復雜,而且要求能夠進行高效的繪制震缭,因此赂毯,它們的UI就不適合在應用程序的主線程中進行繪制。這時候就必須要給那些需要復雜而高效UI的視圖生成一個獨立的繪圖表面拣宰,以及使用一個獨立的線程來繪制這些視圖的UI党涕。

一般來說,每一個窗口在SurfaceFlinger服務中都對應有一個Layer巡社,用來描述它的繪圖表面膛堤。對于那些具有SurfaceView的窗口來說,每一個SurfaceView在SurfaceFlinger服務中還對應有一個獨立的Layer或者LayerBuffer晌该,用來單獨描述它的繪圖表面肥荔,以區(qū)別于它的宿主窗口的繪圖表面。SurfaceFlinger服務負責繪制Android應用程序的UI朝群。SurfaceFlinger服務運行在Android系統(tǒng)的System進程中燕耿,它負責管理Android系統(tǒng)的幀緩沖區(qū)(Frame Buffer)。

SurfaceView原理

官方文檔:

SurfaceView:Provides a dedicated drawing surface embedded inside of a view hierarchy. The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed.

翻譯解釋:

SurfaceView提供一個嵌入視圖層級的專用的繪圖表面姜胖。繪圖表面是在Z軸上有序的誉帅,SurfaceView在宿主窗體的后面。SurfaceView在宿主窗體上“挖”了一個洞谭期,以此來顯示自己的表面堵第。實際上,SurfaceView在其宿主Activity窗口上所挖的“洞”只不過是在其宿主Activity窗口上設置了一塊透明區(qū)域隧出,以顯示自己內(nèi)容

SurfaceView的繪圖表面的創(chuàng)建

我們知道,Activity阀捅、Window胀瞪、View三者緊密聯(lián)系在一起。我們在Activity中設置setContentView()饲鄙,最終會調(diào)用PhoneWindow的setContentView()凄诞。經(jīng)過WindowManagerImpl#addView,WindowManagerGlobal#addView忍级,ViewRootImpl#setView方法帆谍,最頂層視圖DecorView被添加到Window上。最后通過WMS調(diào)用ViewRootImpl#performTraverals方法開始View的測量轴咱、布局汛蝙、繪制流程烈涮。

ViewRootImpl類的成員函數(shù)performTraversals在執(zhí)行的過程中,如果發(fā)現(xiàn)當前窗口的繪圖表面還沒有創(chuàng)建窖剑,或者發(fā)現(xiàn)當前窗口的繪圖表面已經(jīng)失效了坚洽,那么就會請求WindowManagerService服務創(chuàng)建一個新的繪圖表面,同時西土,它還會通過一系列的回調(diào)函數(shù)來讓嵌入在窗口里面的SurfaceView有機會創(chuàng)建自己的繪圖表面讶舰。

雖然SurfaceView不與它的宿主窗口共享同一個繪圖表面,但是它仍然是屬于宿主窗口的視圖結構的一個結點的需了,也就是說跳昼,SurfaceView仍然是會參與到宿主窗口的某些執(zhí)行流程中去。

1 SurfaceView.onAttachedToWindow
image

說明:SurfaceView在Z軸上位置是小于其宿主窗口的Z軸位置的肋乍。為了保證SurfaceView的UI是可見的鹅颊,SurfaceView就需要在其宿主窗口的上面打一個孔出來,實際上就是在其宿主窗口的繪圖表面上設置一塊透明區(qū)域住拭,以便可以將自己顯示出來挪略。SurfaceView類的成員函數(shù)onAttachedToWindow調(diào)用mParent.requestTransparentRegion(SurfaceView.this)去通知父View,當前正在處理的SurfaceView需要在宿主窗口的繪圖表面上打一個孔滔岳,即需要在宿主窗口的繪圖表面上設置一塊透明區(qū)域杠娱。

2. SurfaceView.onWindowVisibilityChanged
image

說明:類SurfaceView調(diào)用updateSurface來更新當前正在處理的SurfaceView。在更新的過程中谱煤,如果發(fā)現(xiàn)當前正在處理的SurfaceView還沒有創(chuàng)建繪圖表面摊求,那么就會請求WindowManagerService服務為它創(chuàng)建一個。

3.SurfaceView.updateRequestedVisibility
image

說明:mWindowVisibility表示SurfaceView的宿主窗口的可見性刘离,mViewVisibility表示SurfaceView自身的可見性室叉。只有當mWindowVisibility和mViewVisibility的值均等于true,且宿主窗口沒有停止硫惕,mRequestedVisible的值才為true茧痕,表示SurfaceView是可見的。

4.SurfaceView.updateSurface

mSurface:這個Surface對象描述的是SurfaceView專有的繪圖表面恼除,在SurfaceView對象創(chuàng)建時就會被實例化踪旷。updateSurface方法根據(jù)實際條件判斷創(chuàng)建或更新mSurface。

SurfaceView的繪制

如何在一個繪圖表面上進行UI繪制豁辉?

1.在繪圖表面的基礎上建立一塊畫布令野,即獲得一個Canvas對象

2.利用Canvas類提供的繪圖方法在前面獲得的畫布上繪制任意的UI

3.最后通過SurfaceFlinger服務將它合成到屏幕上去

SurfaceView如何繪制?

1.通過SurfaceView的getHolder方法獲得SurfaceHolder
2.通過SurfaceHolder的lockCanvas方法獲得Canvas
3.上面會走到Surface的lockCanvas方法獲得Canvas
4.在Canvas上繪制UI
5.通過SurfaceHolder的unlockCanvasAndPost將繪制好的canvas投遞到surface上
6.上面會走到Surface的unlockCanvasAndPost方法
參考文檔

https://developer.android.com/reference/android/view/SurfaceView

https://www.cnblogs.com/plokmju/p/android_SurfaceView.html

https://blog.csdn.net/luoshengyang/article/details/8661317/

http://www.reibang.com/p/700defb6f14b

http://www.reibang.com/p/02800806356c

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末徽级,一起剝皮案震驚了整個濱河市气破,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌餐抢,老刑警劉巖现使,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件低匙,死亡現(xiàn)場離奇詭異,居然都是意外死亡朴下,警方通過查閱死者的電腦和手機努咐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來殴胧,“玉大人渗稍,你說我怎么就攤上這事⊥爬模” “怎么了竿屹?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長灸姊。 經(jīng)常有香客問我拱燃,道長,這世上最難降的妖魔是什么力惯? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任碗誉,我火速辦了婚禮,結果婚禮上父晶,老公的妹妹穿的比我還像新娘哮缺。我一直安慰自己,他們只是感情好甲喝,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布尝苇。 她就那樣靜靜地躺著,像睡著了一般埠胖。 火紅的嫁衣襯著肌膚如雪糠溜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天直撤,我揣著相機與錄音非竿,去河邊找鬼。 笑死谋竖,一個胖子當著我的面吹牛汽馋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播圈盔,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼悄雅!你這毒婦竟也來了驱敲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宽闲,失蹤者是張志新(化名)和其女友劉穎众眨,沒想到半個月后握牧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡娩梨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年沿腰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狈定。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡颂龙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纽什,到底是詐尸還是另有隱情措嵌,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布芦缰,位于F島的核電站企巢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏让蕾。R本人自食惡果不足惜浪规,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望探孝。 院中可真熱鬧笋婿,春花似錦、人聲如沸再姑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽元镀。三九已至绍填,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間栖疑,已是汗流浹背讨永。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留遇革,地道東北人卿闹。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像萝快,于是被迫代替她去往敵國和親锻霎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355