最近公司做新需求, 原来用的老弹幕库, 已经无法满足需要. 迫不得已自己写了一套弹幕库OCBarrage. 这套弹幕库轻量, 可拓展, 高度自定义, 超高性能, 简单易上手.
无论哪家公司软件的性能绝对是衡量APP好坏的重要指标. 之前有一次开会, 我们领导说:”我们写的东西, 有哪些是可以拿的出手,让我们引以为豪的?”. 之前还真就得想一会儿, 现在可以毫不犹豫的说我们的弹幕库绝对是一个好家伙.
做直播类软件核心功能一个是播放器另一个就是弹幕了. 现在iOS开源的弹幕库性能好的不多, 弹幕量稍微大一点, 或者弹幕稍微复杂一点, 就会出现卡顿,这与它的底层实现, 设计策略以及你的使用方法都有关系. 关键是动画单一,无法定制,满足不了动画的多样化需求!OCBarrage正是为解决这些问题而生的!
OCBarrage底层使用Core Animation驱动, Core Graphics绘图, GPU渲染, 性能极高, 哪怕是同时渲染5000条弹幕也不会感觉到卡顿. 开源地址:https://github.com/w1531724247/OCBarrage
(以下测试基于iPhone7真机)
对于全民直播这样的平台来说,在大主播高峰时期的弹幕量是很大的,特别是当主播说一句:“我们现在开始弹幕抽奖”。弹幕量瞬间就会涨的很高!所以对弹幕这一块的要求还是蛮高的.
性能优化原理
弹幕渲染时比较耗性能的点:
1. ######弹幕阴影
主播在户外直播时偶尔会有白色的背景, 而弹幕文字的颜色也是白色的, 这个时候弹幕飘到直播画面的白色区域会导致看不到文字内容. 为了解决这个问题我们通常会给弹幕文字添加一个隐影.以防止这种情况的发生. 然而别小看这几个像素阴影, 它可是性能消耗的大户. 哪怕是用GPU渲染因为是动态的实时的所以也相当吃性能. 在实验的过程中发现如果有文字阴影几十条弹幕就会出现弹幕卡顿, 结果就是弹幕抖动一跳一跳的.
解决办法就是用NSAttributeString
的NSStrokeColorAttributeName
属性设置文字的轮廓颜色替换文字阴影.效果对比如下:
都能解决我们的问题, 但是性能差的可不是一丁半点.
2. ######用CALayer替代UIView展示
与UIView相比CALayer更轻量. 性能更好.系统提供的组件为了保证其通用性, 难免有些冗余.这就是我们优化的空间.
3. ######弹幕文字下面的渐变色背景
彩色弹幕下面的渐变色背景如果用CAGradientLayer实现也是比较耗性能的, 但是如果是用图片呈现的话效果就会好的多, 但是不够灵活, 没关系, 我们都一并解决了.
4. ######将内容合成一张图片展现
将所有的内容呈现在layer上并布局好位置以后将所有的内容合成一张图片展现在barrageCell的layer上, 并删除所有的子subview及sublayer, 以提高性能.
基础类
OCBarrageManager
提供了弹幕渲染视图并负责弹幕引擎的启动, 暂停, 继续及停止.
- renderView
:弹幕呈现的视图.
- renderStatus
:renderView的状态, 开始, 暂停, 结束.
- - (void)resgisterBarrageCellClass:(Class)barrageCellClass withBarrageIndentifier:(NSString *)barrageIndentifier;
:
注册cell类型, 在调用- (void)renderBarrageDescriptor:(OCBarrageDescriptor *)barrageDescriptor;
方法的时候会根据barrageDescriptor的barrageIndentifier
去自动去缓存池中取一个注册的与barrageDescriptor的barrageIndentifier
相同的cell.并将传入的barrageDescriptor赋值给cell的barrageDescriptor属性.
- - (void)start;
:允许接收并渲染弹幕
- - (void)puase;
:暂停弹幕动画. 如果弹幕正在运动则会暂停在当前位置, 并且不会渲染并丢弃新收到的弹幕.
- - (void)resume;
:继续弹幕动画, 并渲染新收到的弹幕.
- - (void)stop;
: 清空当前正在动画的弹幕, 并停止接收渲染新的弹幕.
OCBarrageDescriptor
OCBarrageDescriptor是负责传递弹幕数据的类, 通过barrageIndentifier
找到对应的OCBarrageCell
并将数据展示在OCBarrageCell
上.
- positionPriority
:属性决定了弹幕渲染之后在视图层次上所处的位置, 因为经常会有一些比较重要的弹幕不希望被普通弹幕覆盖, 这个时候可以将重要弹幕的positionPriority
设置的高一点, 这样重要弹幕就可以渲染在普通弹幕的上方而不会被普通弹幕覆盖.
- animationDuration
:动画时间, 这条弹幕从开始到结束的总时间.
- touchAction
:弹幕被点击时候执行的动作.
- bindingOriginY
:本条弹幕在渲染的时候将frame.origin.y固定在bindingOriginY
的位置.>=0
时生效. 可以固定弹幕Y坐标的位置.
OCBarrageCell
呈现弹幕数据的视图.
- idle
:是否是空闲状态, 如果正在运动则为NO, 如果正在缓存池中等待被复用则为YES.
- idleTime
:弹幕动画执行完毕后的时间点. 弹幕动画执行完毕后当前cell会被放进缓存池等待下次复用减少cell创建释放的性能开销. 如果超过5秒没有被复用则会被释放回收内存, 减少内存占用.
- barrageIndentifier
:标识符. 通过这个属性与OCBarrageDescriptor类绑定. 与OCBarrageDescriptor
的barrageIndentifier
对应.
- barrageDescriptor
:当前展示的OCBarrageDescriptor.
- barrageAnimation
:当前cell所执行的动画.
- trackIndex
当前cell所在的弹幕轨道的索引.
- - (instancetype)initWithBarrageIndentifier:(NSString *)barrageIndentifier;
:根据传入的barrageIndentifier创建一个实例.
- - (void)addBarrageAnimationWithDelegate:(id<CAAnimationDelegate>)animationDelegate;
: 子类可以通过重写这个方法为当前cell添加自定义的动画类型.animationDelegate
默认为OCBarrageRenderView
, 可以监听动画执行完毕的事件以便将cell放入缓存池等待下次复用.
- - (void)updateSubviewsData;
:在这个方法里给cell的各个子视图赋值. 也可以在- (void)setBarrageDescriptor:(OCBarrageDescriptor *)barrageDescriptor
方法里给子视图复制. 在这个方法调用以后可以计算一下子视图的的大小及位置.. 也可以在 - (void)layoutSubviews
设置子视图的大小及位置.
- - (void)clearContents;
设置数据前清空一下上次展示的遗留内容.
- - (void)sizeToFit;
:根据子视图的边界自适应大小.
- - (void)convertContentToImageWithSize:(CGSize)contentSize;
: 将当前cell上的子视图的内容绘制成一张图片, 并将生成的图片赋值给当前cell的layer的contents属性展现出来. 这个方法要在设置完数据和布局完子视图的位置之后调用. 这个方法会自动调用- (void)layoutSubviews
因此不可以在layoutSubviews
里调用这个方法不然后造成死循环. 图片生成之后这个方法会执行removeAllSubViewsAndSublayers
删除当前cell上所有的子视图以及子layer以优化展示性能.所以不希望被删除的视图或者layer请在这个方法调用之后添加, 在这个方法执行之后建议将子layer或者子view设置为nil以回收内存.
-
- (void)prepareForReuse;
: 在复用之前要进行的操作可以放在这个方法里执行, 在重写这个方法的时候记得调用一下[super prepareForReuse];
.
OCBarrageRenderView
- animatingCells
: 正在运动的弹幕cell的数组.
- idleCells
: 弹幕动画执行完毕后等待复用的弹幕cell的数组.
- renderPositionStyle
: 新出生的弹幕的位置OCBarrageRenderPositionRandomTracks
, 有弹幕轨道, 新出生的弹幕随机展示在其中一条轨道上, 并且尽量不覆盖在还没呈现完整的正在出来的弹幕上-默认类型.OCBarrageRenderPositionRandom
没有弹幕轨道, y坐标完全随机.OCBarrageRenderPositionIncrease
有弹幕轨道, y坐标循环递增.
- renderStatus
: 引擎状态正在OCBarrageRenderStoped
, 不接收不渲染新弹幕. OCBarrageRenderStarted
接受并渲染新弹幕.OCBarrageRenderPaused
暂停中, 不接收不渲染新弹幕, 已经渲染的弹幕停留在当前所处的位置.
- - (void)resgisterBarrageCellClass:(Class)barrageCellClass withBarrageIndentifier:(NSString *)barrageIndentifier;
: 同OCBarrageManager
的- (void)resgisterBarrageCellClass:(Class)barrageCellClass withBarrageIndentifier:(NSString *)barrageIndentifier;
.
- - (nullable OCBarrageCell *)cellWithBarrageIndentifier:(NSString *)barrageIndentifier;
: 根据传入的barrageIndentifier从缓存池或者注册的cell列表中返回一个cell.
- - (void)fireBarrageCell:(OCBarrageCell *)barrageCell;
: 发射弹幕.
- - (void)start;
: 同OCBarrageManager
.
- - (void)puase;
: 同OCBarrageManager
.
- - (void)resume;
: 同OCBarrageManager
.
- - (void)stop;
: 同OCBarrageManager
.
OCBarrageTrackInfo
弹幕轨道信息类.
- trackIndex
: 弹幕轨道所在的位置.
- trackIdentifier
: 弹幕轨道标识符, 不同类型的弹幕, 不重用同一条轨道.
- nextAvailableTime
:下次可用时间, 主要是用来防止某条轨道上刚出现了一条弹幕还没有完全展现在屏幕上, 后面这条轨道上就又来了一条弹幕盖在之前的那个弹幕上, 影响用户体验.
- barrageCount
: 当前轨道的弹幕数量.
当然写到这里依然还有优化的空间, 后续会继续优化, 欢迎各位仁人志士共同探讨指点.
开源地址:https://github.com/w1531724247/OCBarrage
from:https://blog.csdn.net/w1531724247/article/details/77619111