淘宝直播组件调度、复用和治理

本文主要介绍了淘宝直播组件进行的三大优化方向:组件调度、组件复用和组件治理。通过这些优化,旨在提升直播间性能与用户体验。最终,这些优化在技术指标(如 CPU 使用率、卡顿率)和业务数据(如人均观看时长、转化率)上都取得了显著提升。文章也提到了未来将继续优化的方向,包括布局层级优化和组件内部逻辑精简等。

01 背景 

直播间的各种业务形态,主要都是通过直播间组件的方式实现的。 比如评论区组件、互动层组件、顶部和底部基础组件、商品小卡组件等等。

组件主要的行为包括:

  • 创建视图 (inflate View)
  • 绑定直播间 detail 接口信息完成初始化(比如组件渲染 DX 也是在这个阶段)
  • 上屏渲染
  • 离屏销毁并释放资源

基于端性能&用户体验的考虑,我们对直播间的业务组件进行了三个方向的优化:

  1. 组件调度是指基于产品设计动线对组件进行延迟加载(或者说分批加载),并且组件的加载支持暂停、恢复和取消。
  2. 组件复用是指组件本身的复用(组件对象和组件对应视图 View 的复用)以及组件内部元素的复用(比如基于列表 RecyclerView 组件的 RecyclerView.ViewHolder, DX View 的复用等等)
  3. 组件治理是指减少非必要组件在直播间初始化时执行的耗时操作。优化组件布局,减少 inflate 耗时与组件上屏渲染时 measure & layout 的耗时。

02 组件调度加载 

从业务层面上,组件动线加载的目标是引导用户进房关注点,减少进房时过多组件加载(展示)产生的干扰,进而增加用户观看时长。

从技术层面上讲,组件的分批加载,可以减少短期内过多组件同时加载产生的主线程消息过多、负载较高,带来的卡顿、画面不流畅(TextureView 播放)以及 CPU 负载高与功耗的问题。

淘宝直播组件调度、复用和治理

从产品设计上,我们将直播间划分为多个区块

  • 基础框架:顶部主播信息区&底部工具栏区
  • 右下角分区:商品小卡&商品小卡热卖氛围
  • 左下角分区:评论区&重要事件通知区
  • 左上角分区:榜单、亲密度、公告&活动 Banner
  • 右上角分区:更多直播&互动层挂件入口

进房后,根据图中序号的顺序,对不同的组件配置不同的延迟策略,产生组件动线加载的效果~

技术实现

在技术实现上,我们做了两件事

1.对直播间组件进行了重构。 直播间组件基于组合模式,这很像 Android 里面 View 与 ViewGroup 的关系。根节点组件在加载业务组件的时候,我们将业务组件加载的生命周期拆成了三段式

  • 组件注册:创建组件对象实例并注册到根组件下
  • 创建组件视图:inflate View,并将 View 挂载在视图树上。 针对一些复杂耗时的布局,为了减少主线程耗时卡顿,会在子线程上执行。
  • 绑定直播间 detail 接口返回信息,完成初始化。

注意这里组件的注册和组件的视图创建,并不是每个直播间生命周期都会调用。实际上在直播间上下滑复用时,只会有组件绑定直播间 detail 接口返回信息而复用组件的视图。 关于组件复用我们后面会讲。

2.组件加载调度器(FluidLauncher)的设计。 FluidLauncher 是我们基于直播间组件加载设计的调度器,它负责调度 FluidTask,组件的三段式也被封装到了 FluidTask 中。

FluidLauncher 支持的能力包括:

  • 支持 APK 内置配置 / 代码设置或者远程化发布的配置控制组件加载的延迟,实现组件的动线
  • 支持任务主线程执行或者子线程执行(基于虚拟线程池,我们一般会将视图创建时 inflate View 之类比较耗时的操作放到子线程上)
  • 支持任务的可暂停、可恢复、可取消。

任务的暂停/恢复/取消

任务的暂停,恢复和取消是为了上下滑的流畅度。 直播间组件的加载都是比较耗时的。 如果在上下滑过程中,依然在执行组件的加载,可能会导致上下滑动画的不流畅。 如果在当前直播间划走后依然执行当前直播间组件加载的话,可能会由 Message 泄漏导致内存泄漏。

  • 暂停:用户还是滑动屏幕进行上下滑直播间交互时,如果任务没有执行,则会从主线程上被移除并暂存,这是任务的暂停
  • 恢复:如果用户没有滑动到下一坑,当前直播间暂存的任务会被重新抛到主线程的 MessageQueue 中等待调度执行
  • 取消:如果直播间被划走,那么对应直播间的任务都会被移除并且不会再被抛到主线程 MessageQueue 队列。

03 组件的复用

组件的复用

上面我们说到直播间组件加载的三段式中,组件的注册和组件视图创建并不是每次上下滑都会执行的,因为存在直播间组件的复用。 在这里,直播间组件的复用基于 RecyclerView 内部的复用机制。

RecyclerView 内部,存在多级缓存。在这里,生效的主要是 mCachedView 这一层缓存与 RecycledPool 这一层缓存。简单讲一下原理,RecyclerView 在需要用一个 View(或者说 ViewHolder)填补当前位置的时候,它并不总会调用 onCreateViewHolder 或者 onBindViewHolder。 它会先尝试从 mCachedView 这一层缓存判断当前 position 有没有对应的 ViewHolder,如果有则使用,这个时候既不需要调用 onCreateViewHolder,也不需要调用 onBindViewHolder。 否则,RecyclerView 会去 RecycledPool 中判断对应的 ViewType 有没有缓存的 ViewHolder(在这里 ViewType 我们可以近似理解为都是普通直播间的 ViewType),如果能拿到对应的缓存 ViewHolder,也不会再调用 onCreateViewHolder 而只会调用 onBindViewHolder。 对应的源码可以参考:

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java;drc=4b83f9cd9587f3e590afdfa82243dfcffd9b7750;l=6940?q=RecyclerView

RecyclerView#tryGetViewHolderForPositionByDeadline 方法

这里有一个近似的架构图。在这里,我们省略掉一些中间层。这样我们就可以理解了 ViewHolder 复用的时候,会让直播间组件也得到复用。

淘宝直播组件调度、复用和治理

而我们上面讲到,组件加载三段式中的第二段,是 inflate View,这里的 BaseFrame#mContainerView 就是对应组件 inflate 之后创建的视图。 于是组件在复用的时候,对应的视图 View 也就得到了复用。

组件内部元素的复用

其实组件元素内部复用包含了很多东西的复用,在这里我们重点讲其中两个:

  • 通过复用 DX View 加快 DX 的渲染,优化卡顿与 CPU
  • 列表类组件设置公用的 RecycledPool 复用里面的 ViewHolder

DX复用加快渲染

在这里我们简单贴一张 DX 渲染管线的示意图

淘宝直播组件调度、复用和治理

在渲染阶段(也就是真实视图树的创建阶段),会首先判断 WidgetNode 有没有对应的 View,如果有则复用,否则会需要重新创建。

淘宝直播组件调度、复用和治理

从 Trace 来看,DX View 复用目标就是优化掉 DX-Pipeline-CreateView 这个 Section 通过对组件内部 DX View 复用后相同模板单次渲染性能明显提升。

不开启复用,渲染耗时 21ms

淘宝直播组件调度、复用和治理

开启复用,渲染耗时 8ms

淘宝直播组件调度、复用和治理

列表类组件ViewHolder复用

直播间的评论区组件和右侧挂件,视图基于 RecyclerView。基于直播间上下滑的特性与前面提到的 RecyclerView 本身的复用机制。自然想到可以让上下滑中的所有直播间的 RecyclerView, 都共用同一个 RecycledPool。 这样,在上个直播间创建的 ViewHolder(包括里面的 itemView),自然可以在下个直播间被复用。

///GlobalContext 中管理评论区/列表组件RecycledPool 
private final RecyclerView.RecycledViewPool chatItemRecyclerPool = new RecyclerView.RecycledViewPool();

private final RecyclerView.RecycledViewPool rightItemRecyclerPool = new RecyclerView.RecycledViewPool();


/// 评论区
mChatRv.setRecycledViewPool(mFrameContext.getGlobalContext().getChatItemRecyclerPool());

/// 右侧挂件      
mRightComponentsRv.setRecycledViewPool(mFrameContext.getGlobalContext().getRightItemRecycle

不过这里我们要单独强调一点,RecyclerView 在重新设置 Adapter 的时候,会清空原先 RecycledPool 中的 ViewHolder,这里具体的分析就不细讲了,在实操的时候需要关注一下。

在这里,直播间调用的是这个方法

public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews)

这样的话不会清空 RecycledPool 里面的 ViewHolder,保证 ViewHolder 可以继续复用~

04 组件治理 

前面我们说到了直播间加载过程中有三段式的设计,包括组件的注册、inflate View 和绑定 detail 接口信息。 直播间总共有 64 个组件,但是实际上进入直播间时,屏幕中我们看不到这么多元素。

这就牵扯出了关于组件治理的思考:

如果一个组件不展示,那么它还需要 inflate View 吗,这样岂不是破坏了 View Stub 最初的设计? 如果一个组件不展示,那它绑定 detail 接口信息产生的耗时操作,能不能迁移到组件真实展示的时候?

我们在这里对直播间的组件用一个思维导图进行简单的分类:

淘宝直播组件调度、复用和治理

只有基础组件,我们可以理解为每个直播间都需要加载展示。而内容化组件,作为内容化直播间这种特殊业务形态下的功能组件,其实在大部分直播间都不需要展示的。 而工具类组件,也可以考虑将耗时的操作改到用户操作时(比如 onClick)。

直播间的组件治理分为两期,跨度为 1 个月。总共治理的组件有 20+个,包括了多人连麦、PK、多人语音房等内容化业务的组件,也包括了互动面板,投屏组件,更多直播等工具类组件。

05 结果 

组件是直播间业务的核心,通过对直播间组件的优化,我们在 CPU、卡顿等方面取得了显著收益。同时在用户观看时长和转化率都有一定幅度的提升。

06 不足与展望 

  • 前面我们说到过组件治理的一个方向是组件渲染的优化,目的是通过优化视图布局层级,使用更稳定、优化的布局方式,提升组件上屏渲染的性能。但是目前这个方向投入还不足,直播间依然还存在非常深的嵌套层级,以及低效的刷新方式。 这个我们会在新的财年去解决。
  • 组件内部还有一些耗时、冗余、可优化的业务逻辑。从业务侧,我们也在持续治理中。

07 团队介绍

本文作者周宗,来自淘天集团-直播用户终端技术团队。本团队专注于通过技术创新为用户打造流畅、互动性强且高效的直播间观看与消费体验。依托KMM、前端alive互动组件等技术,我们构建了高效的日迭代方案,能够快速响应产品需求,完善各个业务功能,并高效支持各类运营活动的上线。同时,我们持续关注直播间的功耗、卡顿率以及播放体验,设立了端性能专项,致力于不断优化淘宝直播的性能与稳定性。此外,我们积极探索AI技术的应用,将其融入日常开发的代码生成、排障等环节,全面拥抱AI技术革命。通过技术驱动,我们助力业务发展,与业务共同成长,为用户和平台创造更大价值。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论