架構(gòu)6--SurfaceView 和 GLSurfaceView

轉(zhuǎn)自:https://source.android.com/devices/graphics/?hl=zh-cn

SurfaceView 和 GLSurfaceView

Android 應(yīng)用框架界面是以使用 View 開頭的對(duì)象層次結(jié)構(gòu)為基礎(chǔ)春缕。所有界面元素都會(huì)經(jīng)過一個(gè)復(fù)雜的測(cè)量和布局過程,該過程會(huì)將這些元素融入到矩形區(qū)域中,并且所有可見 View 對(duì)象都會(huì)渲染到一個(gè)由 SurfaceFlinger 創(chuàng)建的 Surface(在應(yīng)用置于前臺(tái)時(shí)穆壕,由 WindowManager 進(jìn)行設(shè)置)菌赖。應(yīng)用的界面線程會(huì)執(zhí)行布局并渲染到單個(gè)緩沖區(qū)(不考慮 Layout 和 View 的數(shù)量以及 View 是否已經(jīng)過硬件加速)。

SurfaceView 采用與其他視圖相同的參數(shù),因此您可以為 SurfaceView 設(shè)置位置和大小剩盒,并在其周圍填充其他元素呆瞻。但是台夺,當(dāng)需要渲染時(shí),內(nèi)容會(huì)變得完全透明痴脾;SurfaceView 的 View 部分只是一個(gè)透明的占位符颤介。

當(dāng) SurfaceView 的 View 組件即將變得可見時(shí),框架會(huì)要求 WindowManager 命令 SurfaceFlinger 創(chuàng)建一個(gè)新的 Surface。(這個(gè)過程并非同步發(fā)生滚朵,因此您應(yīng)該提供回調(diào)冤灾,以便在 Surface 創(chuàng)建完畢后收到通知。)默認(rèn)情況下辕近,新的 Surface 將放置在應(yīng)用界面 Surface 的后面韵吨,但可以替換默認(rèn)的 Z 排序,將 Surface 放在頂層移宅。

渲染到該 Surface 上的內(nèi)容將會(huì)由 SurfaceFlinger(而非應(yīng)用)進(jìn)行合成归粉。這是 SurfaceView 的真正強(qiáng)大之處:您獲得的 Surface 可以由單獨(dú)的線程或單獨(dú)的進(jìn)程進(jìn)行渲染,并與應(yīng)用界面執(zhí)行的任何渲染隔離開漏峰,而緩沖區(qū)可直接轉(zhuǎn)至 SurfaceFlinger糠悼。您不能完全忽略界面線程,因?yàn)槟匀恍枰c Activity 生命周期相協(xié)調(diào)浅乔,并且如果 View 的大小或位置發(fā)生變化倔喂,您可能需要調(diào)整某些內(nèi)容,但是您可以擁有整個(gè) Surface童擎。與應(yīng)用界面和其他圖層的混合由 Hardware Composer 處理滴劲。

新的 Surface 是 BufferQueue 的生產(chǎn)者端,其消費(fèi)者是 SurfaceFlinger 層顾复。您可以使用任意提供 BufferQueue 的機(jī)制(例如班挖,提供 Surface 的 Canvas 函數(shù))來更新 Surface,附加 EGLSurface 并使用 GLES 進(jìn)行繪制芯砸,或者配置 MediaCodec 視頻解碼器以便于寫入萧芙。

合成與硬件縮放

我們來仔細(xì)研究一下dumpsys SurfaceFlinger。當(dāng)在 Nexus 5 上假丧,以縱向方向在 Grafika 的“播放視頻 (SurfaceView)”活動(dòng)中播放電影時(shí)双揪,采用以下輸出;視頻是 QVGA (320x240):

type? ? |? ? ? ? ? source crop? ? ? ? ? ? ? |? ? ? ? ? frame? ? ? ? ? name------------+-----------------------------------+--------------------------------? ? ? ? HWC | [? ? 0.0,? ? 0.0,? 320.0,? 240.0] | [? 48,? 411, 1032, 1149] SurfaceView? ? ? ? HWC | [? ? 0.0,? 75.0, 1080.0, 1776.0] | [? ? 0,? 75, 1080, 1776] com.android.grafika/com.android.grafika.PlayMovieSurfaceActivity? ? ? ? HWC | [? ? 0.0,? ? 0.0, 1080.0,? 75.0] | [? ? 0,? ? 0, 1080,? 75] StatusBar? ? ? ? HWC | [? ? 0.0,? ? 0.0, 1080.0,? 144.0] | [? ? 0, 1776, 1080, 1920] NavigationBar? FB TARGET | [? ? 0.0,? ? 0.0, 1080.0, 1920.0] | [? ? 0,? ? 0, 1080, 1920] HWC_FRAMEBUFFER_TARGET

列表順序是從后到前:SurfaceView 的 Surface 位于后面包帚,應(yīng)用界面層位于其上渔期,其次是處于最前方的狀態(tài)欄和導(dǎo)航欄。

源剪裁值表示 Surface 緩沖區(qū)中 SurfaceFlinger 將顯示的部分渴邦。應(yīng)用界面會(huì)獲得一個(gè)與顯示屏的完整尺寸 (1080x1920) 一樣大的 Surface疯趟,但是由于渲染和合成將被狀態(tài)欄和導(dǎo)航欄遮擋的像素毫無意義,因此將源剪裁為一個(gè)矩形(上自離頂部 75 個(gè)像素谋梭,下至離底部 144 個(gè)像素)信峻。狀態(tài)欄和導(dǎo)航欄的 Surface 較小,并且源剪裁描述了一個(gè)矩形(起點(diǎn)位于左上角 (0,0) 并且會(huì)橫跨其內(nèi)容)瓮床。

框架值指定在顯示屏上顯示像素的矩形盹舞。對(duì)于應(yīng)用界面層产镐,框架會(huì)與源剪裁匹配,因?yàn)槲覀儠?huì)將與顯示屏同樣大小的圖層的一部分復(fù)制(或疊加)到另一個(gè)與顯示屏同樣大小的圖層中的相同位置踢步。對(duì)于狀態(tài)欄和導(dǎo)航欄癣亚,兩者的框架矩形大小相同,但是位置經(jīng)過調(diào)整贾虽,所以導(dǎo)航欄出現(xiàn)在屏幕底部逃糟。

SurfaceView 層容納我們的視頻內(nèi)容。源剪裁與視頻的大小相匹配蓬豁,而 SurfaceFlinger 了解該信息,因?yàn)?MediaCodec 解碼器(緩沖區(qū)生成器)正在將同樣大小的緩沖區(qū)移出隊(duì)列菇肃〉胤啵框架矩形具有完全不同的尺寸:984x738。

SurfaceFlinger 通過縮放(根據(jù)需要放大或縮兴霭)緩沖區(qū)內(nèi)容來填充框架矩形蟆技,以處理大小差異。之所以選擇這種特定尺寸斗忌,是因?yàn)樗哂信c視頻相同的寬高比 (4:3)质礼,并且由于 View 布局的限制(為了美觀,在屏幕邊緣處留有一定的內(nèi)邊距)织阳,因此應(yīng)盡可能地寬眶蕉。

如果您在同一 Surface 上開始播放不同的視頻,底層 BufferQueue 會(huì)將緩沖區(qū)自動(dòng)重新分配為新的大小唧躲,而 SurfaceFlinger 將調(diào)整源剪裁造挽。如果新視頻的寬高比不同,則應(yīng)用需要強(qiáng)制重新布局 View 才能與之匹配弄痹,這將導(dǎo)致 WindowManager 通知 SurfaceFlinger 更新框架矩形饭入。

如果您通過其他方式(如 GLES)在 Surface 上進(jìn)行渲染,則可以使用SurfaceHolder#setFixedSize()調(diào)用設(shè)置 Surface 尺寸肛真。例如谐丢,您可以將游戲配置為始終采用 1280x720 的分辨率進(jìn)行渲染,這將大大減少填充 2560x1440 平板電腦或 4K 電視機(jī)屏幕所需處理的像素?cái)?shù)蚓让。顯示處理器會(huì)處理縮放乾忱。如果您不希望給游戲加上水平或垂直黑邊,您可以通過設(shè)置尺寸來調(diào)整游戲的寬高比凭疮,使窄尺寸為 720 像素饭耳,但長尺寸設(shè)置為維持物理顯示屏的寬高比(例如,設(shè)置為 1152x720 來匹配 2560x1600 的顯示屏)执解。有關(guān)此方法的示例寞肖,請(qǐng)參閱 Grafika 的“硬件縮放練習(xí)程序”活動(dòng)纲酗。

GLSurfaceView

GLSurfaceView 類提供幫助程序類,用于管理 EGL 上下文新蟆、線程間通信以及與 Activity 生命周期的交互觅赊。這就是其功能。您無需使用 GLSurfaceView 來應(yīng)用 GLES琼稻。

例如吮螺,GLSurfaceView 創(chuàng)建一個(gè)渲染線程,并配置 EGL 上下文帕翻。當(dāng)活動(dòng)暫停時(shí)鸠补,狀態(tài)將自動(dòng)清除。大多數(shù)應(yīng)用都不需要知道 EGL嘀掸,便可通過 GESurfaceView 使用 GLES紫岩。

在大多數(shù)情況下,GLSurfaceView 非常實(shí)用睬塌,可簡化 GLES 的使用泉蝌。但在某些情況下,卻會(huì)造成妨礙揩晴。請(qǐng)?jiān)谟杏脮r(shí)使用勋陪,無用時(shí)棄用。

SurfaceView 和 Activity 生命周期

當(dāng)使用 SurfaceView 時(shí)硫兰,使用主界面線程之外的線程渲染 Surface 是很好的做法诅愚。不過,這樣就會(huì)產(chǎn)生一些與線程和 Activity 生命周期之間的交互相關(guān)的問題瞄崇。

對(duì)于具有 SurfaceView 的 Activity呻粹,存在兩個(gè)單獨(dú)但相互依賴的狀態(tài)機(jī):

狀態(tài)為 onCreate/onResume/onPause 的應(yīng)用

已創(chuàng)建/更改/銷毀的 Surface

當(dāng) Activity 開始時(shí),將按以下順序獲得回調(diào):

onCreate

onResume

surfaceCreated

surfaceChanged

如果回?fù)羲昭校鷮⒌玫剑?/p>

onPause

surfaceDestroyed(在 Surface 消失前調(diào)用)

如果旋轉(zhuǎn)屏幕等浊,Activity 將被消解并重新創(chuàng)建,而您將獲得整個(gè)周期摹蘑。您可以通過檢查isFinishing()告知屏幕快速重新啟動(dòng)筹燕。啟動(dòng)/停止 Activity 可能非常快速衅鹿,從而可能導(dǎo)致surfaceCreated()實(shí)際上是在onPause()之后發(fā)生撒踪。

如果您點(diǎn)按電源按鈕鎖定屏幕,則只會(huì)得到onPause()(沒有surfaceDestroyed())大渤。Surface 仍處于活躍狀態(tài)制妄,并且渲染可以繼續(xù)。如果您繼續(xù)請(qǐng)求泵三,甚至可以持續(xù)獲得 Choreographer 事件耕捞。如果您使用強(qiáng)制變向的鎖屏衔掸,則當(dāng)設(shè)備未鎖定時(shí),您的 Activity 可能會(huì)重新啟動(dòng)俺抽;但如果沒有敞映,您可以使用與之前相同的 Surface 脫離屏幕鎖定。

當(dāng)使用具有 SurfaceView 的單獨(dú)渲染器線程時(shí)磷斧,會(huì)引發(fā)一個(gè)基本問題:線程壽命是否依賴 Surface 或 Activity 的壽命振愿?答案取決于鎖屏?xí)r您想要看到的情況:(1) 在 Activity 啟動(dòng)/停止時(shí)啟動(dòng)/停止線程,或 (2) 在 Surface 創(chuàng)建/銷毀時(shí)啟動(dòng)/停止線程弛饭。

選項(xiàng) 1 與應(yīng)用生命周期交互良好冕末。我們?cè)趏nResume()中啟動(dòng)渲染器線程,并在onPause()中將其停止侣颂。當(dāng)創(chuàng)建和配置線程時(shí)栓霜,會(huì)顯得有點(diǎn)奇怪,因?yàn)橛袝r(shí) Surface 已經(jīng)存在横蜒,有時(shí)不存在(例如,在使用電源按鈕切換屏幕后销凑,它仍然存在)丛晌。我們必須先等待 Surface 完成創(chuàng)建,然后再在線程中進(jìn)行一些初始化操作斗幼,但是我們不能簡單地在surfaceCreated()回調(diào)中進(jìn)行操作澎蛛,因?yàn)槿绻粗匦聞?chuàng)建 Surface,將不會(huì)再次觸發(fā)蜕窿。因此谋逻,我們需要查詢或緩存 Surface 狀態(tài),并將其轉(zhuǎn)發(fā)到渲染器線程桐经。

注意:在線程之間傳遞對(duì)象時(shí)要小心毁兆。最好通過處理程序消息傳遞 Surface 或 SurfaceHolder(而不僅僅是將其填充到線程中),以避免多核系統(tǒng)出現(xiàn)問題阴挣。有關(guān)詳細(xì)信息气堕,請(qǐng)參閱Android SMP Primer

選項(xiàng) 2 非常具有吸引力畔咧,因?yàn)?Surface 和渲染器在邏輯上互相交織茎芭。我們?cè)趧?chuàng)建 Surface 后啟動(dòng)線程,避免了一些線程間通信問題誓沸,也可輕松轉(zhuǎn)發(fā) Surface 已創(chuàng)建/更改的消息梅桩。當(dāng)屏幕鎖定時(shí),我們需要確保渲染停止拜隧,并在未鎖定時(shí)恢復(fù)渲染宿百;要實(shí)現(xiàn)這一點(diǎn)趁仙,可能只需告知 Choreographer 停止調(diào)用框架繪圖回調(diào)。當(dāng)且僅當(dāng)渲染器線程正在運(yùn)行時(shí)犀呼,我們的onResume()才需要恢復(fù)回調(diào)幸撕。盡管如此,如果我們根據(jù)框架之間的已播放時(shí)長進(jìn)行動(dòng)畫繪制外臂,我們可能發(fā)現(xiàn)坐儿,在下一個(gè)事件到來前存在很大的差距;應(yīng)使用一個(gè)明確的暫停/恢復(fù)消息宋光。

注意:有關(guān)選項(xiàng) 2 的示例貌矿,請(qǐng)參閱 Grafika 的“硬件縮放練習(xí)程序”。

這兩個(gè)選項(xiàng)主要關(guān)注如何配置渲染器線程以及線程是否正在執(zhí)行罪佳。一個(gè)相關(guān)問題是逛漫,終止 Activity 時(shí)(在onPause()或onSaveInstanceState()中)從線程中提取狀態(tài);在此情況下赘艳,選項(xiàng) 1 最有效酌毡,因?yàn)樵阡秩酒骶€程加入后,不需要使用同步基元就可以訪問其狀態(tài)蕾管。

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 3.0 License, and code samples are licensed under theApache 2.0 License. For details, see ourSite Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 八月 24, 2017.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枷踏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子掰曾,更是在濱河造成了極大的恐慌旭蠕,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旷坦,死亡現(xiàn)場(chǎng)離奇詭異掏熬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)秒梅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門旗芬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人番电,你說我怎么就攤上這事岗屏。” “怎么了漱办?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵这刷,是天一觀的道長。 經(jīng)常有香客問我娩井,道長暇屋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任洞辣,我火速辦了婚禮咐刨,結(jié)果婚禮上昙衅,老公的妹妹穿的比我還像新娘。我一直安慰自己定鸟,他們只是感情好而涉,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著联予,像睡著了一般啼县。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沸久,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天季眷,我揣著相機(jī)與錄音,去河邊找鬼卷胯。 笑死子刮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窑睁。 我是一名探鬼主播挺峡,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼担钮!你這毒婦竟也來了沙郭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤裳朋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后吓著,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲤嫡,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年绑莺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了暖眼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纺裁,死狀恐怖诫肠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情欺缘,我是刑警寧澤栋豫,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站谚殊,受9級(jí)特大地震影響丧鸯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嫩絮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一丛肢、第九天 我趴在偏房一處隱蔽的房頂上張望围肥。 院中可真熱鬧,春花似錦蜂怎、人聲如沸穆刻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氢伟。三九已至,卻和暖如春篮愉,著一層夾襖步出監(jiān)牢的瞬間腐芍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國打工试躏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猪勇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓颠蕴,卻偏偏與公主長得像泣刹,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子犀被,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容