“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

新弹幕引擎架构设计,提高业务开发效率

  • 2020-05-29
  • 本文字数:3892 字

    阅读完需:约 13 分钟

新弹幕引擎架构设计,提高业务开发效率

一、背景简介

优酷旧有的弹幕功能经过长期业务迭代,添加各种需求,代码耦合越来越严重,一个 View 显示里用 if else 兼容了各种各样的显示样式;为了同时使用在 iPhone 版和 iPad 版 APP 里,又加了不少机型判断。最终导致新增功能越来越困难,于是重新设计一套灵活好用的弹幕引擎框架提上日程。


本文主要分享优酷 APP 目前正在使用的新版弹幕引擎库,与播放器解耦、可独立使用,无第三方依赖,打包后大小仅有 60 多 KB。

二、定制能力

1)支持定制弹幕子 View 显示;


2)支持定制各项参数;


3)支持屏蔽某些弹幕;


4)可由业务实现新的弹幕显示形式,不局限于默认的从右向左滚动、置顶、置底。

三、架构设计简介

弹幕引擎库的主要作用是显示弹幕的滚动、或者置顶置底然后淡出,它需要知道一批数据、某条数据使用某种子 View 渲染以及使用某种排版形式,同时还需要一些必要的参数,如行高。


首先定义几个名词:


排版类型:目前支持从右向左滚动、置顶、置底,业务可自己实现其它特殊的排版;


UI 风格:指单条数据表现出来的 ui 样式,库内置一个简单的 Label 显示,业务可自己实现其它的 ui 样式,如左边一个图片右边文字。


为了提供较强的定制能力,满足各种需求,引擎库使用了插件注册方式提供各种能力,包括默认的排版类型和 UI 风格也是通过插件注册的,下面是整个库的架构设计图:



引擎库对外导出的内容包含图里上层的 DanmakuView、Data Driver,及下层的 DataPlugin、 Setting Plugin、Layout Plugin、Ui Plugin,中间的一层业务不可见。


DanmakuView 是弹幕子 View 显示的父容器,业务可把它放在任何一个业务页面,并给它提供合适的 frame,它提供了注册插件,开始暂停动画、直接塞数据、获取当前显示的弹幕 View、数据、重新布局等方法。


整个流程是业务通过左侧数据驱动或者直接通过 DanmakuView 给到数据后,数据被分类缓存在 Data Cache Manager 里,内部布局 Engine 开始启动内部定时器,从 Cache 拿数据,有了数据后通过 ViewPool 取到一个新建的或者之前用过被回收的相同类型子 View ,然后从 SettingPlugin 拿到当前需要的参数,一并交给子 View 去计算宽高,然后再尝试调用 LayoutPlugin 的方法去排版,如果此时 DanmakuView 容器里能排版,则通知子 View 去渲染更新内部元素,如果不能排版则等待下一个定时器事件、或直到有新一批数据给到后被丢弃。

四、插件简介

所有插件创建后都通过 YKDanmakuView 的 registerPlugin 方法进行注册。


所有插件的属性和方法都带有 ykdm_前缀避免与业务的属性或方法冲突。


  1. UI 风格插件


Ui Plugin: 提供渲染数据的子 View,ui 插件要实现 YKDanmakuUiStylePlugin 协议,通过 uiType 表明自身负责创建哪种数据的弹幕子 View ,同时子 View 类要实现 YKDanmakuItemViewProtocol 协议,通过 viewSize 方法告知引擎当前子 View 的宽高,引擎确认剩余空间是否够排版,如可以则调用 renderView 方法由插件负责显示子 view 内部元素, viewSize 方法内部不建议去真正的刷新内部 view 显示,因为此时可能因为排版不下而放弃,多余的刷新显示动作浪费性能。



上面截图中可以实现多个 ui 插件用来表现多种不同的风格,有带图的,有纯文本的,当然相似的也可以用一种 ui 插件,然后 view 内部根据数据显示隐藏部分子 view。


  1. 排版插件


Layout Plugin: 当 View 创建完成后,布局插件用来实现具体的排版类型,库里已经默认实现了从右向左滚动、置顶、置底,插件需实现 YKDanmakuLayoutPlugin 协议,通过 layoutType 区分是哪种排版类型。


  1. 参数设置插件


Setting Plugin: 用来提供引擎库必要的一些基本参数以及某条弹幕数据是否需要过滤掉不显示,需实现 YKDanmakuSettingPlugin 协议,基本参数通过 YKDanmakuSettingParam 对象告知引擎库,包含行高、显示几行、滚动持续时间、置顶置底的淡出动画时间等参数。


  1. 数据相关插件


显示子 View、排版、参数都有了,还差一个不可缺少的内容,数据从哪儿来?


1)数据基本格式


数据会通过数组提供给弹幕引擎,数据基类都需实现 YKDanmakuItemInfoProtocol 协议中要求的几个方法:


a)layoutType 提供布局类型:告知引擎使用哪种 LayoutPlugin 排版,内置 kYKDanmakuLayoutTypeScroll、kYKDanmakuLayoutTypeTop、kYKDanmakuLayoutTypeBottom,对应着从右向左滚动、置顶、置底;


b)uiType 提供 UI 风格类型,告知引擎使用哪种 UIPlugin 去创建 View 及计算大小、渲染;


c)forceShow 此数据是否是需要强制显示,用在 vip 用户、自己发的弹幕等高优先级的情况,当有下一批数据来更新替换上一批数据时,上一批数据中如存在 forceShow 为 True 的数据,那么即使排版不下此数据也会强制排版(可能会与之前排版的弹幕部分重叠);


d)contentText 弹幕的文本内容,当未提供 UIPlugin 时,默认使用此属性显示一个文本 View。


2)数据提供形式


数据的提供形式可以分为两大类,一种是一次性的简单给予,一种是持续不断的给予或者有定制需求。


a)如果业务是在图文如漫画业务上把用户评论作为弹幕显示,数据一般是一次性给的,那么可以直接通过 DanmakuView 的 setDataArray 方法将弹幕数据提供给引擎,引擎按序用完数据结束显示;


b)除上面这种简单情况,如果数据需要一批批的给,比如像视频播放器一样每秒更新数据,或者数据更新不是替换而是追加,或者数据要循环使用,都可以用下面这种用法:


对于第二种稍复杂的情况,需创建一个 Data Driver(继承自:YKDanmakuBaseDataDriver)用来驱动数据更新,配合 DataPlugin 提供数据,DataPlugin 需实现协议 YKDanmakuDataPlugin。


Data Driver 可以接受播放器的通知(比如每秒一次播放进度更新)、或者内置定时器、或从实时通道接收原始数据、或一次性驱动,总之当需要更新数据时调用基类的 triggerFetchDataWithParams 方法带上业务自己规则的参数,此时 DataPlugin 的 triggerFetchDataWithParams 实现会被调用,参数也会传递过来, DataPlugin 根据参数的不同通过不同的方式取得解析好的数据,如访问后端 api、读取离线缓存、使用参数中提供的原始 json 数据等等,然后通过 callback 同步或者异步返回给弹幕引擎。


DataPlugin 其中几个方法的作用:


a)dataDriver: 引擎内部关联 DataEngine 使用,通过这个方法返回 Data Driver 实例即可;


b) dataRefreshMode: 可选,表示此数据更新是清除旧数据再添加(YKDmDataRefreshMode_Replace)、或是追加(YKDmDataRefreshMode_Append),默认 Replace;


c)recyclableData:可选,表示提供的数据使用完成后是否继续从头开始使用,比如漫画或者图文型弹幕可能提供一批数据后,后续反复使用。默认 NO。

五、交互需求建议


如上图点击后被点击的弹幕暂停,同时显示一个小面板(小面板业务创建并添加到父 View 容器中)。


1)如果弹幕子 View 需要点击交互,那么此子 View 需要重载 hitTest:withEvent:方法,不然无法响应点击;


- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {   point = [self convertPoint:point toView:self.superview];   if ([self.layer.presentationLayer hitTest:point] != nil) {      return self;   }   return nil;}
复制代码


2)业务可在 UI Plugin 里 constructView 方法或者 View 创建 init 代码里注册各种点击事件,当用户点击后进行需要的操作,如唤起输入框回复此弹幕,或者显示一个小面板;


3)YKDanmakuItemViewProtocol 里 renderView 方法里 YKDanmakuLayoutParam 参数的有属性表明是否是重新使用此子 view 显示一个新数据,当更新参数或者父 View frame 变化导致当前在屏幕上的子 View 重新刷新时此参数是 false,此参数可用来做曝光埋点等业务。同时可选方法 notifyShowComplete 表示此子 view 显示完成即将被回收,可以用来清除 text、image 或者配合计算某种 text 显示了多少次,做些彩蛋之类的需求。

六、性能优化建议

1)Setting Plugin 的 settingParamByLayoutParam 方法调用很频繁,实现需尽可能高效,比如计算一次后缓存起来供下次使用;


2)当 YKDanmakuView 的 frame 改变后,会自动对当前已经显示的弹幕子 View 重新排版和刷新,比如一些业务横竖屏不同状态下子 View 文字大小、滚动速度会有不同,此时业务无需再调用 YKDanmakuView 里的 reloadSetting 方法重复刷新;


3)参数设置中的滚动时间变化后后续排版会自动使用新参数,无需调用 YKDanmakuView 里的 reloadSetting 方法;


4)在 frame 不变的情况下,如要单独更新文字颜色、字体大小、显示行数、过滤弹幕等可调用 reloadSetting 方法,为提搞性能,刷新标记参数可选择以下其一:


a)kYKDanmakuLayoutFlagColor 只重绘颜色,不影响大小,此模式跳过大小计算逻辑,提高性能;


b)kYKDanmakuLayoutFlagSize 更新了字体大小,一般要影响子 view 的整体布局,但不更新颜色;


c)kYKDanmakuLayoutFlagFilter 通过 settingPlugin 的 needFilter 方法控制整个 view 显示或隐藏、或者调整了整体显示行数,只隐藏或者显示子 View,不重新计算大小及刷新显示;


d)kYKDanmakuLayoutFlagAll 需要完整计算位置及渲染逻辑。


5)与上述对应,子 View 的重新渲染代码建议这样写:


- (void)ykdm_renderViewWithLayoutParam:layoutParam...... {   if ([layoutParam needChangeAll]) {      //对内容修改,如label 赋值,对imageView 设置图片等等   }
if ([layoutParam needChangeColor]) { //需要更新颜色,可设置文字颜色,各种背景色等等 }
if ([layoutParam needChangeSize]) { //需要调整view 各项大小,如字号更新,图片大小更新 }
if (layoutParam.firstLayout) { //用新数据进行渲染,埋点等 }}
复制代码


作者 | 阿里文娱高级无线开发工程师 趋势


2020-05-29 17:061855

评论

发布
暂无评论
发现更多内容

gdb调试工具使用

正向成长

gdb

fil云算力挖矿系统开发成功案例丨fil云算力挖矿源码设计

系统开发咨询1357O98O718

Spring Cloud Gateway 和Webflux 请求参数非法字符处理

sN0wpeak

Java Spring Cloud SpringcloudGateway

FILPool云算力挖矿系统开发源码案例

系统开发咨询1357O98O718

腾讯iOS开发要达到咋样的水准?

一意孤行的程序员

flutter 职业规划 音视频 ios开发 iOS逆向

苦难中开花|靠谱点评

无量靠谱

IPFS挖矿系统开发详情案例丨IPFS挖矿矿机源码平台

系统开发咨询1357O98O718

时间很贵,有趣才对

小天同学

读书笔记 读书感悟 好书推荐 有趣 4月日更

Rust从0到1-代码组织-Packages和Crates

rust Package crate

产业级项目实战配合技术讲解,百度零门槛AI开发实训营重磅开营

百度大脑

百度 AI

YouTube如何下载1080P的高清视频带声音?5种方法可以搞定

右手牵

下载 视频 youtube

apk优化,996页阿里Android面试真题解析火爆全网,附面试答案

欢喜学安卓

android 程序员 面试 移动开发

Spark内存管理与调优

小舰

4月日更

ORC格式和PARQUET格式介绍

五分钟学大数据

hive 存储 4月日更

万字长文,38 图爆肝 Redis 基础!

一个优秀的废人

redis 数据结构 集合 哈希表 跳表

【全网首发】2021 年从iOS初级到大牛,涵盖大部分iOS开发知识体系,不会没收藏吧!

一意孤行的程序员

swift 面试 ios开发 底层应用开发 知识分享

android面试题及答案,我的阿里春招之路分享,附面试答案

欢喜学安卓

android 程序员 面试 移动开发

工作三年,小胖连 HashMap 源码都没读过?真的菜!

一个优秀的废人

扩容 hashmap 源码剖析 HashMap底层原理 加载因子

不忘初心,中级领导力修炼

IT民工大叔

领导力 管理

华为云薛浩:走进视频“新时代”

华为云开发者联盟

云原生 视频 华为云 vr 图形处理

英特尔Ice Lake对于高性能计算市场意味着什么

E科讯

随机而转 当机而动|靠谱点评

无量靠谱

手机淘宝轻店业务 Serverless 研发模式升级实践

Serverless Devs

阿里云 Serverless 云原生 淘宝

数字化转型:打破孤岛以提高数据安全性

龙归科技

数字化

美国工程院士、谷歌首席架构师 杰夫·迪恩(JeffDean) John 易筋 ARTS 打卡 Week 45

John(易筋)

ARTS 打卡计划

快来,告诉你一个赚钱的秘诀:金钱的秘密

帅安技术

赚钱 秘密 金钱的秘密

重读《重构2》- 以查询取代临时变量

顿晓

重构 4月日更

浅谈BSS3.0产品“守成”之策中 • 业务提升篇

鲸品堂

系统架构 性能调优

Redis的AOF方案(Redis持久化)

大数据技术指南

redis 4月日更

面试4轮字节Java研发岗,最终拿下2-1Offer(原题复盘)

Java架构追梦

Java 字节跳动 面试

阿里P8带你学习:一次线上服务高 CPU 占用优化实践

学Java关注我

Java 编程 架构 程序人生 软件架构

新弹幕引擎架构设计,提高业务开发效率_文化 & 方法_阿里巴巴文娱技术_InfoQ精选文章