在可滾動(dòng)的容器中播放視頻其實(shí)是有一定難度的——對(duì)設(shè)備資源的壓力可能會(huì)導(dǎo)致視頻丟幀,這會(huì)使?jié)L動(dòng)效果變的很差。此外,我們不希望用戶等待視頻加載,我們也不希望它在播放中碰到緩沖,因此視頻播放器需要快速啟動(dòng)并流暢運(yùn)行。縱觀市場(chǎng)上的各種設(shè)備,這些問(wèn)題在 Android 上的挑戰(zhàn)還是很大的。
我們最近完成了 Android 平臺(tái)的新聞提要技術(shù)遷移,由我們的開(kāi)源 UI 渲染框架 Litho 提供技術(shù)支持。Litho 支持異步布局和細(xì)粒度的回收利用,這不僅有助于優(yōu)化新聞提要使其更有效地渲染內(nèi)容,而且可以使我們的代碼更健壯、更易于擴(kuò)展。我們希望將這些改進(jìn)帶到視頻功能上來(lái),以改善 Facebook 用戶的播放體驗(yàn),同時(shí)也為設(shè)計(jì)新用例的工程師們提供幫助。
構(gòu)建一個(gè)視頻 UI 元素比我們所做的其他 UI 組件更具挑戰(zhàn)性。視頻組件使用了 Litho 的一些其他組件不需要的高級(jí)特性,同時(shí)也促使 Litho 創(chuàng)建了一些新的特性。新的 Litho 視頻組件引入了一些改進(jìn),從新聞提要的滾動(dòng)性能到更靈活的設(shè)計(jì),這些設(shè)計(jì)可以很容易地在不同的視頻功能中重用。這篇文章描述了我們?cè)?Android 應(yīng)用程序中重寫視頻播放器時(shí)所面臨的挑戰(zhàn),以及 Litho 如何幫助克服這些困難的。
視頻新聞
新聞提要支持多種視頻新聞?lì)愋?。有些類型的行為和外觀與其他的有所不同。視頻附件是一種常見(jiàn)的新聞?lì)愋?,其中有常?guī)視頻、Facebook Live 視頻、360 度視頻、gif 或其他類型的視頻,它們會(huì)附在一個(gè)普通的文章上。
其他的視頻新聞?lì)愋涂梢圆シ派傻囊曨l,贊助商的信息,或者短動(dòng)畫。
想要支持所有這些變種會(huì)導(dǎo)致代碼難以維護(hù)和測(cè)試。我們提供視頻附件使用的主視頻視圖類叫 VideoAttachmentView。對(duì)于一些新聞?lì)愋?,我們必須擴(kuò)展這個(gè)視圖,以便重用和定制它來(lái)適應(yīng)設(shè)計(jì)的需要。在某些情況下,我們不能在派生類中進(jìn)行所有的更改,只能創(chuàng)建一個(gè)單獨(dú)的視圖類,這意味著需要將通用邏輯移到幫助類中。這進(jìn)一步使代碼復(fù)雜化了。
新聞提要的滾動(dòng)性能也會(huì)受到影響。RecyclerView 僅從相同類的類型中回收視圖,這意味著不能為常規(guī)視頻附件新聞?lì)愋突厥障?SponsoredVideoAttachmentView 這樣的視圖,盡管 SponsoredVideoAttachmentView 繼承至 VideoAttachmentView。由于這種低效率的原因,RecyclerView 需要分配更多的視圖來(lái)容納不同的新聞?lì)愋?,從而?dǎo)致了更多的內(nèi)存占用。另外,在視頻視圖展現(xiàn)在屏幕上之前,分配行為就要被觸發(fā),這增加了它無(wú)法及時(shí)完成的可能性,甚至可能會(huì)導(dǎo)致丟幀。即使在一個(gè)新的布局中重用相同的 VideoAttachmentView 來(lái)優(yōu)化它的表現(xiàn),我們也需要?jiǎng)?chuàng)建一個(gè)新的視圖類型。因?yàn)槲覀兞硗馓砑恿艘粚?,這也使得視圖層次結(jié)構(gòu)變的更深了。視圖層次越深,渲染的時(shí)間就越長(zhǎng)。
設(shè)計(jì)視頻組件
Litho 用一種叫做“組件”的單位來(lái)定義 UI。它支持兩種主要類型的組件:MountSpec 和 LayoutSpec。MountSpec 定義了一個(gè) UI 構(gòu)建塊,例如一個(gè)文本或圖像組件。LayoutSpec 定義一個(gè)包含一個(gè)或多個(gè)組件的布局。在 Litho 中,使用組件結(jié)構(gòu)來(lái)構(gòu)造更復(fù)雜的組件是很常見(jiàn)的做法。LayoutSpec 可以定義一個(gè)包含其他 LayoutSpec 和 MountSpec 組件的布局,MountSpec 可以呈現(xiàn)一個(gè)特定的視圖或繪制視圖。
視頻組件是一個(gè) UI 構(gòu)建塊,這意味著我們需要一個(gè) MountSpec 來(lái)定義它。一種方法是創(chuàng)建一個(gè) MountSpec 來(lái)呈現(xiàn) VideoAttachmentView,但我們還需要為其它擴(kuò)展視圖創(chuàng)建另一個(gè) MountSpec, 如 SponsoredVideoAttachmentView。雖然這做也是可行的,但最終我們還會(huì)遇到相同的重用性和可維護(hù)性的問(wèn)題。另一種方法是創(chuàng)建一個(gè) MountSpec 只呈現(xiàn)簡(jiǎn)單的視頻視圖而不是 VideoAttachmentView,并將其添加到一個(gè)特定的新聞?lì)愋偷?LayoutSpec 中。下圖演示了新組件之間的關(guān)系:
CoreVideoComponent 是一個(gè)有著最簡(jiǎn)特性的任何視頻新聞都需要的 MountSpec。
@MountSpec
public class CoreVideoComponentSpec {
@OnCreateMountContent
static SimpleVideoView onCreateMountContent(ComponentContext context) {
return new SimpleVideoView(context);
}
@OnPrepare
static void onPrepare(
ComponentContext context,
@Prop VideoParams videoParams) {
prefetchVideo(videoParams);
}
@OnMount
static void onMount(
ComponentContext context,
SimpleVideoView videoView,
@Prop VideoParams videoParams) {
initVideoPlayback(videoView, videoParams);
}
@OnUnmount
static void onUnmount(
ComponentContext context,
SimpleVideoView videoView,
@Prop VideoParams videoParams) {
cleanupVideoPlayback(videoView, videoParams);
}
...
}
CoreVideoComponent 是 AutoplayVideoComponent 的子類,該組件是一個(gè)用于在新聞提要中注冊(cè)視頻的 LayoutSpec。所有新聞提要中的視頻都是在自動(dòng)播放管理器上注冊(cè)的,但并不是所有的視頻都需要自動(dòng)播放功能 (例如,全屏視頻播放器中的視頻)。
@LayoutSpec
public class AutoplayVideoComponentSpec {
@OnCreateLayout
static Component onCreateLayout(
ComponentContext c,
@Prop VideoParams videoParams) {
registerVideoForAutoplay(videoParams);
return CoreVideoComponent.create(c)
.videoParams(videoParams)
.build();
}
...
}
最后, 我們將自動(dòng)播放組件作為子類添加到 VideoAttachmentComponent 中。這個(gè)組件將一個(gè)視頻附件數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為一個(gè)通用的視頻組件都能理解的屬性。
public class VideoAttachmentComponentSpec {
@OnCreateLayout
static Component onCreateLayout(
ComponentContext c,
@Prop VideoAttachmentInfo videoAttachmentInfo) {
final VideoParams videoParams = createVideoParams(videoAttachmentInfo);
return AutoplayVideoComponent.create(c)
.videoParams(videoParams)
.build();
}
...
}
與 VideoAttachmentView 相比,這個(gè)設(shè)計(jì)提供了更多的靈活性。這些組件中的任何一個(gè)都可以添加到另一個(gè) LayoutSpec 中,創(chuàng)建一個(gè)更復(fù)雜的組件并擴(kuò)展它的功能或 UI 設(shè)計(jì)。Litho 鼓勵(lì)使用嵌套組件,以及組件組合,以構(gòu)建更強(qiáng)大的功能。Litho 以最優(yōu)的渲染性能優(yōu)化了布局樹,構(gòu)建出了扁平的視圖結(jié)構(gòu)。
下面是一個(gè)創(chuàng)建視頻附件組件的示例,該組件顯示底部的水印:
@LayoutSpec
public class WatermarkVideoAttachmentComponentSpec {
@OnCreateLayout
static Component onCreateLayout(
ComponentContext c,
@Prop VideoAttachmentInfo videoAttachmentInfo) {
return Column.create(c)
.flexShrink(0)
.alignContent(YogaAlign.FLEX_START)
.child(
VideoAttachmentComponent.create(c)
.videoAttachmentInfo(videoAttachmentInfo))
.child(
Text.create(c)
.text("Powered by Litho")
.textColorRes(android.R.color.holo_green_light)
.textSizeDip(25)
.paddingDip(YogaEdge.LEFT, 5)
.positionType(YogaPositionType.ABSOLUTE)
.positionDip(YogaEdge.BOTTOM, 0)
.positionDip(YogaEdge.START, 0))
.build();
}
}
新組件通過(guò)將其添加為子組件來(lái)重新使用視頻附件組件的所有代碼和 UI。
性能改進(jìn)
除了支持更加靈活的設(shè)計(jì)之外,Litho 還提供了一些屬性和特性,幫助我們優(yōu)化新聞提要中的視頻播放和整個(gè)應(yīng)用的整體性能。
資源回收利用
Android 內(nèi)置的 RecyclerView 可以基于視圖的類型將其保存在不同的緩存池中,這對(duì)于創(chuàng)建了很多不同類型視圖的用戶界面來(lái)說(shuō)可能會(huì)是一個(gè)問(wèn)題。
相比之下,Litho 的回收系統(tǒng)復(fù)用了更小的用戶界面構(gòu)建模塊,比如文本或圖片,而不是整個(gè)視圖。通過(guò)使用一個(gè)核心視頻組件,同樣的視圖可以被循環(huán)使用于所有的視頻新聞?lì)愋?。更有效的回收利用減少了對(duì)象的分配,進(jìn)而提高了滾動(dòng)性能。
預(yù)分配
新聞提要的第一個(gè)視頻新聞不能循環(huán)使用預(yù)先存在的視頻視圖,因?yàn)橹皼](méi)有視圖。當(dāng)兩個(gè)視頻新聞同時(shí)出現(xiàn)在屏幕上時(shí)也需要注意:一個(gè)視頻視圖可以從以前的新聞中回收,但是第二個(gè)視圖需要新建。當(dāng) RecyclerView 需要分配一個(gè)新的視圖對(duì)象,特別是像視頻視圖那樣的復(fù)雜視圖時(shí),會(huì)帶來(lái)丟幀的風(fēng)險(xiǎn)。我們希望優(yōu)化這種情況,因此我們?cè)?Litho 中創(chuàng)建了預(yù)分配功能。
通過(guò)向 MountSpec 注解中添加一些屬性,我們可以讓 Litho 提前創(chuàng)建一些實(shí)例。當(dāng)滾動(dòng)瀏覽新聞提要中的第一個(gè)視頻新聞時(shí),預(yù)分配的視頻視圖可以極大地提高滾動(dòng)性能。
@MountSpec(poolSize = 3, canPreallocate = true) public class CoreVideoComponentSpec { ... }生命周期
MountSpec 有一些實(shí)用且簡(jiǎn)單的生命周期回調(diào)方法。這些足以讓我們將大部分視頻播放邏輯封裝在組件中。在 Litho 之前,這個(gè)邏輯會(huì)被分散到不同的類中,由一個(gè)單獨(dú)的控制器觸發(fā)。視頻組件中的主要回調(diào)方法包括:
onPrepare- 開(kāi)始預(yù)取視頻。在視頻組件出現(xiàn)之前,在后臺(tái)線程上觸發(fā)。
onMount- 初始化視頻播放器。組件首次配置其視圖屬性時(shí)觸發(fā)。
onUnmount- 清除視頻播放器,為下一次使用做準(zhǔn)備。當(dāng)視頻滾動(dòng)走時(shí)被觸發(fā)。
LayoutSpec 有一個(gè)主要的回調(diào):onCreateLayout()。它的主要目的是構(gòu)造 LayoutSpec 的布局,但是它也可以為其子組件準(zhǔn)備資源。例如,封面照片 LayoutSpec 可以在上面創(chuàng)建一個(gè)帶有視頻和封面照片的布局,同時(shí)還可以觸發(fā)封面照片的預(yù)抓取,所有這些都是在同一個(gè)回調(diào)方法中進(jìn)行的。
MountSpec 還支持另一個(gè)實(shí)用的回調(diào):shouldUpdate()。當(dāng) RecyclerView 的適配器被更新時(shí),它可以重新綁定所有的子視圖,并獲得所有可見(jiàn)的組件并重新加載 (觸發(fā) onUnmount 和 onMount)。對(duì)于簡(jiǎn)單的組件,這沒(méi)有明顯的影響,但是重新配置一個(gè)視頻播放器就會(huì)是一個(gè)比較繁重的操作。這個(gè)回調(diào)是在 Litho 重新加載組件之前調(diào)用的,如果你覺(jué)得它沒(méi)有必要的話 (例如,加載相同的視頻),我們可以選擇跳過(guò)它。
@ShouldUpdate(onMount = true) static boolean shouldUpdate(@Prop Diff可測(cè)試性videoParamsDiff) { return videoParamsDiff.getNext().videoId != videoParamsDiff.getPrevious().videoId; }
新組件的模塊化有助于更輕松地測(cè)試視頻播放邏輯。Litho 提供了一個(gè)測(cè)試框架,可以在單元測(cè)試中模擬組件的生命周期。通過(guò)將更多的視頻播放邏輯封裝在這些組件中,我們可以測(cè)試和驗(yàn)證我們以前無(wú)法使用的復(fù)雜場(chǎng)景。此外,通過(guò)使用組合而不是繼承來(lái)擴(kuò)展功能,會(huì)更安全、更容易維護(hù)。
結(jié) 論
通過(guò)使用傳統(tǒng)的 Android 視圖,Litho 幫助我們通過(guò)一次構(gòu)建就可以提升視頻實(shí)現(xiàn)的性能、效率、可擴(kuò)展性和可維護(hù)性。該視頻組件在整個(gè) Facebook 的 Android 應(yīng)用程序中提升了 20% 的滾動(dòng)性能,同時(shí)也改善了我們的冷啟動(dòng)時(shí)間,使我們的內(nèi)存崩潰率降低了 2.5%,這都是更有效的內(nèi)存管理的結(jié)果。代碼的改進(jìn)不僅改善了 Facebook 的體驗(yàn),還讓工程師有了更多在 Facebook 應(yīng)用中創(chuàng)建新的視頻體驗(yàn)的余地。
Litho 允許我們?cè)?Android 上編寫高度優(yōu)化的 UI。Litho 于 2017 年 4 月開(kāi)源,此后一直在成長(zhǎng)。在去年年底,我們啟動(dòng)了 Sections 項(xiàng)目,它是基于 Litho 構(gòu)建的用于編寫高度優(yōu)化的列表界面的 API。轉(zhuǎn)換到 Litho 和 Sections 后,通常會(huì)有大幅度的性能提升,比如滾動(dòng)性能提升可能高達(dá) 42%。使用 Litho 很簡(jiǎn)單并且文檔豐富??梢圆榭?Litho 網(wǎng)站和 GitHub 頁(yè)面上的代碼示例、教程和高級(jí)指南。
-
Android
+關(guān)注
關(guān)注
12文章
3980瀏覽量
132712
原文標(biāo)題:Facebook構(gòu)建高性能Android視頻組件實(shí)踐之路
文章出處:【微信號(hào):livevideostack,微信公眾號(hào):LiveVideoStack】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
高性能的視頻采集電路圖
鴻蒙原生頁(yè)面高性能解決方案上線OpenHarmony社區(qū) 助力打造高性能原生應(yīng)用
FPGA構(gòu)建高性能DSP
[分享資料]+Android高薪之路-Android程序員面試寶典
分享高性能Android應(yīng)用開(kāi)發(fā)超清版PDF
高性能Android應(yīng)用開(kāi)發(fā)
如何阻止Facebook視頻自動(dòng)發(fā)出聲音
FPGA構(gòu)建高性能DSP
NI推出最新高性能軟件和硬件組件,構(gòu)建完整的測(cè)試系統(tǒng)
Facebook將進(jìn)軍視頻領(lǐng)域
使用樹莓派構(gòu)建 Slurm 高性能計(jì)算集群:分步指南!

Facebook構(gòu)建高性能Android視頻組件實(shí)踐之路
評(píng)論