写点什么

使用 Litho 改进 News Feed 上的 Android 视频表现

作者:CatTalk
  • 2021 年 11 月 29 日
  • 本文字数:4634 字

    阅读完需:约 15 分钟

使用 Litho 改进 News Feed 上的 Android 视频表现

原文链接:Improving Android video on News Feed with Litho


随着移动设备上的视频消费快速增长,Facebook 移动工程师面临着高效呈现内容的新挑战。与简单的 UI 元素(如文本和照片)相比,视频需要更多资源。它们使用使 CPU 保持忙碌的解码器;他们分配大量内存进行设置;他们使用更多的网络带宽从服务器下载视频数据。在像 News Feed 这样的可滚动容器中播放视频尤其具有挑战性——设备资源的压力有丢帧的风险,这可能会导致抖动的滚动体验。另外,我们不希望人们等待视频加载,也不希望它在播放过程中缓冲,因此视频播放器需要快速启动并运行流畅。鉴于市场上的设备种类繁多,所有这些挑战都在 Android 上加剧。


我们最近完成了 Android 上的News Feed 迁移,由我们的开源 UI 渲染框架Litho提供支持。Litho 支持异步布局和细粒度回收,这不仅有助于优化 News Feed 以更有效地呈现内容,而且还使我们的代码更健壮且更易于扩展。我们希望为视频带来这些好处,以改善 Facebook 用户和设计新用例的工程师的播放体验。


构建视频 UI 元素比我们处理过的其他 UI 部分更具挑战性。视频组件利用了 Litho 中其他组件不需要的高级功能,并激发了以前不存在的功能。新的 Litho 视频组件引入了多项改进,从 News Feed 的滚动性能到可以轻松重复用于各种视频功能的更灵活的设计。这篇文章介绍了我们在 Android 应用程序中重写视频播放器时面临的挑战,以及 Litho 如何帮助克服这些挑战。

新闻提要中的视频

新闻提要支持多种视频故事类型。有些人的行为和外观与其他人不同。常见的故事类型是视频附件,其中常规视频、Facebook 直播视频、360 度视频、GIF 或其他格式附加到常规帖子。



其他视频故事类型可以播放生成的视频、赞助消息或短动画。



支持所有这些变体导致代码难以维护和测试。我们的视频附件的主要视频视图被称为VideoAttachmentView。对于一些故事类型,我们必须扩展该视图,以便重用和自定义它以适应设计。在某些情况下,我们无法在派生类中进行所有更改,而必须创建一个单独的视图类,这意味着将公共逻辑移至辅助类。这进一步使代码复杂化。


News Feed 的滚动性能也受到影响。RecyclerView 仅从相同的类类型回收视图,这意味着它不能像SponsoredVideoAttachmentView常规视频附件故事那样回收视图,即使SponsoredVideoAttachmentView扩展VideoAttachmentView. 由于这种低效率,RecyclerView 分配了更多的视图来适应不同的故事类型,从而导致更大的内存占用。此外,分配在视频视图需要出现在屏幕上之前触发,增加了它无法及时准备好并丢失一帧或多帧的机会。即使VideoAttachmentView在新布局中重复使用相同的内容来修改外观VideoAttachmentView,我们需要创建一个新的视图类型。因为我们正在添加另一个层,这也使视图层次结构更深。视图层次结构越深,渲染所需的时间就越长。

设计视频组件

Litho 使用称为“组件”的单元来定义 UI。它支持两种主要类型的组件:MountSpec 和 LayoutSpec。甲MountSpec定义的 UI 建筑砌块,如文本或图像成分。甲LayoutSpec定义含有一个或多个部件的布局。Litho 中的常见做法是使用组件组合来构建更复杂的组件。LayoutSpec 可以定义包含其他 LayoutSpec 和 MountSpec 组件的布局,而 MountSpec 呈现特定视图或可绘制对象。


视频组件是一个 UI 构建块,这意味着我们需要一个 MountSpec 来定义它。一种选择是创建一个呈现 的 MountSpec VideoAttachmentView,但我们随后需要为其他扩展视图创建一个 MountSpec,例如SponsoredVideoAttachmentView. 虽然这可行,但我们最终会遇到相同的重用和可维护性问题。另一种方法是创建一个 MountSpec 来呈现一个简单的视频视图而不是VideoAttachmentView,并将其添加到一个 LayoutSpec 中,以针对特定的故事类型对其进行调整。下图说明了新组件之间的关系:



CoreVideoComponent是一个 MountSpec,具有任何视频故事所需的最少功能。



@MountSpecpublic 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作为子项添加到AutoplayVideoComponentLayoutSpec 中,该 LayoutSpec 注册了要在新闻提要中自动播放的视频。动态消息中的所有视频都注册到自动播放管理器,但并非应用程序中的所有视频都需要自动播放功能(例如,全屏视频播放器中的视频)。



@LayoutSpecpublic class AutoplayVideoComponentSpec {
@OnCreateLayout static Component onCreateLayout( ComponentContext c, @Prop VideoParams videoParams) { registerVideoForAutoplay(videoParams);
return CoreVideoComponent.create(c) .videoParams(videoParams) .build(); } ...}
复制代码


最后,我们将自动播放视频组件作为子组件添加到VideoAttachmentComponent. 该组件将视频附件数据结构转换为更通用的视频组件理解的属性。



@LayoutSpecpublic class VideoAttachmentComponentSpec {
@OnCreateLayout static Component onCreateLayout( ComponentContext c, @Prop VideoAttachmentInfo videoAttachmentInfo) { final VideoParams videoParams = createVideoParams(videoAttachmentInfo);
return AutoplayVideoComponent.create(c) .videoParams(videoParams) .build(); } ...}
复制代码


这种设计为我们提供了更多的灵活性VideoAttachmentView。这些组件中的任何一个都可以添加到另一个 LayoutSpec,从而创建一个更复杂的组件来扩展其功能和/或 UI 设计。Litho 鼓励使用嵌套组件以及组件组合来构建更大的功能。布局树由 Litho 优化以获得最佳渲染性能,创建更平坦的视图


这是创建在底部显示水印的视频附件组件的示例:



@LayoutSpecpublic 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(); }}
复制代码


新组件通过将其添加为子组件来重用视频附件组件的所有代码和 UI。


性能改进

除了实现更灵活的设计外,Litho 还具有一些属性和功能,可帮助我们优化新闻提要中的视频播放和整个应用程序的总体性能。

回收

Android 的内置 RecyclerView 根据它们的类型将视图保存在不同的池中,这对于具有许多不同类型的 UI 来说可能是有问题的。



相比之下,Litho 的回收系统重用较小的 UI 构建块,例如文本或图像,而不是整个视图。通过使用一个核心视频组件,可以为所有视频故事类型回收相同的视图。更有效的回收减少了对象分配并提高了滚动性能。


预分配

News Feed 中的第一个视频故事无法回收预先存在的视频观看次数,因为之前没有观看次数。当两个视频故事出现在屏幕上时,这也很重要:一个视频视图可以从前一个故事中回收,但第二个视图需要在那时创建。当 RecyclerView 需要分配新的视图对象时,尤其是像视频视图这样复杂的视图对象时,会带来丢帧的风险。我们想优化这种情况,因此我们在 Litho 中创建了预分配功能


通过向 MountSpec 注释添加几个属性,我们指示 Litho 提前分配一些实例。滚动浏览新闻提要中的第一个视频故事时,预分配视频视图可显着提高滚动性能。



@MountSpec(poolSize = 3, canPreallocate = true)public class CoreVideoComponentSpec { ...}
复制代码

生命周期

MountSpec 有一些有用且简单的生命周期回调方法。这些让我们将大部分视频播放逻辑封装在组件中。在 Litho 之前,此逻辑分布在不同的类中,并由单独的控制器触发。视频组件中的主要回调方法包括:


  • onPrepare

  • — 开始预取视频。在视频组件出现在屏幕上之前在后台线程上触发。

  • onMount

  • — 初始化视频播放器。组件第一次配置其视图时触发。

  • onUnmount

  • — 清理视频播放器以备下次使用。当视频滚动离开时触发。


LayoutSpec 有一个主要回调:onCreateLayout()。它的主要目的是构建 LayoutSpec 的布局,但也可以为其子组件准备资源。例如,封面照片 LayoutSpec 可以创建一个带有视频和封面照片的布局,同时还会触发封面照片的预取,所有这些都在相同的回调方法下。


MountSpec 支持另一个有用的回调:shouldUpdate()。当 RecyclerView 的适配器更新时,它可以重新绑定其所有子视图并重新安装所有可见组件(触发onUnmount,然后onMount)。对于更简单的组件,没有明显的影响,但重新配置视频播放器是一项繁重的操作。在 Litho 重新安装组件之前调用此回调,让我们有机会在不必要时跳过它(例如,在安装相同视频时)。



@ShouldUpdate(onMount = true)static boolean shouldUpdate(@Prop Diff<VideoParams> videoParamsDiff) { return videoParamsDiff.getNext().videoId != videoParamsDiff.getPrevious().videoId;}
复制代码

可测试性

新组件的模块化有助于更轻松地测试视频播放逻辑。Litho 带有一个测试框架,可以在单元测试中模拟组件的生命周期。通过在这些组件中封装更多的视频播放逻辑,我们可以测试和验证以前无法实现的复杂场景。此外,通过使用组合而不是继承来扩展功能更安全且更易于维护。

结论

与使用传统 Android 视图构建的视频实现相比,Litho 帮助提高了视频实现的性能、效率、可扩展性和可维护性。视频组件使我们在 Facebook for Android 应用程序中的滚动性能提高了 20%。它还缩短了我们的冷启动时间并将我们的总内存不足崩溃减少了 2.5%,这都是更高效的内存管理的结果。代码中的改进不仅改善了 Facebook 体验,还为工程师提供了更多的余地来创建跨 Facebook 应用程序的新视频体验。


Litho 允许我们在 Android 中编写高度优化的 UI。它于 2017 年 4 月开源,此后一直没有停止增长。去年年底,我们推出了Sections,这是一个用于编写高度优化的列表表面的 API,它构建在 Litho 之上。我们看到了一些到 Litho 和 Sections 的转换的显着性能改进,例如滚动性能改进高达 42%。开始使用 Litho 很简单,而且有据可查。查看 Litho网站GitHub 页面以获取代码示例、教程和高级指南。

用户头像

CatTalk

关注

公众号:CatTalk 2018.02.26 加入

一线互联网公司技术专家,主要关注Android、Java、大前端,从事AIOT端智能业务落地

评论

发布
暂无评论
使用 Litho 改进 News Feed 上的 Android 视频表现