而且都拿礼品动画效果做的特别炫酷.由于要以列面临优化送礼物内存暴涨的题材。

送红包作为观众打赏支持主播的一模一样栽艺术, 也是直播应用的同坏收益来源,
每个直播平台都包含送礼就等同效果, 并且都管红包动画效果做的专门炫酷.
如此之动画效果又追加配美人或帅哥主播的均等词”谢谢某某某送的百般飞机~”,
是不是想都来硌小震动, 感觉瞬间变成了全场的点子?

由于要在列蒙优化送礼金内存暴涨的问题,所以在网上查找了众多素材。最后找到一个略带伙伴的,非常不易,解决了自我之题目。
借鉴这篇稿子:https://www.jianshu.com/p/4135b7de046c

正文主要讲述的尽管是很礼动效的落实. 全文共3000配左右,
大概阅读时间吗5~12分钟.

UIImageView自带的animationImages来举行行帧播放会出现内存激增的情况.

先放上仍序列帧播放方案实现之动画片引擎FXAnimationEngine,
Demo中贯彻了直播间礼物队列、礼物配置、礼物列表,
另外还分别就此动画引擎和原生Core Animation去播放序列帧动画为开比较.

1.加载图片的不二法门优化
imageNamed方法创建对象的步调如下:

然后国际惯例, 上少摆设图

因图片文件称于缓存池中搜寻图片数,如有,则创建对象并赶回;
若无在,则于bundle中加载图片数,创建对象并赶回;
苟相应的图形数未有,返回nil。

梦城堡

不缓存:imageWithContentsOfFile:
得传入图片文件的真名(全路线+文件称)。
没辙加载Images.xcassets中的图形。

天使

死图片最好下imageWithContentsOfFile,内存会释放。


这就是说闹什么法好避吗? 可也每次播放到啦一样幅时即便夺加载那一帧底图纸,
即每次就加载一摆设图片及内存中. 这样当播放到下一样布置图纸时,
上一样摆设图片就任外引用, 系统自会对该进行释放.

平、直播应用礼物动画的科普方案

不过个人了解, 实现iOS侧动画配置化常见方案来如下几种植:

iOS方案 优点 缺点
Core Animation(此处不计CAKeyFrameAnimation) 效果流畅逼真 安卓需重新实现; 配置化成本高, 需自定义模型、协议、转换方法等(iOS侧已有现成工具, 某几家直播公司想必也有自己的动画配置化工具); 不解决动态配置问题, 则只能随包更新.
序列帧播放(CAKeyframeAnimation、CADisplaylink、ImageView等) 设计哥工具可直接导出动画序列帧图片, 简单易用; 多平台兼容 效果略差; 图片帧数多易导致资源大
Cocos2d-x 效果好; 多平台兼容 学习成本; 相应动画制作工具; 必须引入Cocos2d库;
Lottie 横跨三端, iOS, Android, React Native. 设计师可以完全按照自己的想法设计. 无需考虑实现这一块. 内存占用? 作者本人尚未使用过, 不敢妄自评论

可看看, 序列帧播放方案是中间最为简单易行易行的一个. 在我看来,
花椒直播因此之就是即刻套方案, 他们每一个动画,
都见面相应一个配备文件config.ini及对相应动画的持有班帧图片.

谢谢兴趣之恋人可以变至最后一有的礼物资源的下载策略、资源目录结构等连带内容,
更建议尝试去追一下花椒、映客等主流直播应用之bundle目录以及document中之资源.

当时就是是最最简易可行的同法方案.
但是我们无能为力借助CAAnimation及其派生类CAKeyframeAnimation来促成就同样方案,
因为有的图形都见面解码导致占用大量的内存.

次、序列帧播放方案实施

只是咱好透过CADisplayLink来促成该方案,
选CADisplayLink的因由是它们比较NSTimer精度要后来居上多,
正常情形下CADisplayLink的回调会以屏幕每次刷新时触发,
即一般1/60秒触发一样浅, 适合用来做UI的重绘,
因此得以经其来周期性的更迭关键帧图片, 从而达到播放动画的机能.
那么具体怎么开吗?

2.1 实现方式

队帧播放动画一方案的求实实现必须能满足以下需要:

  1. 图形展示: CALayer、UIImageView
  2. 比如时间间隔逐帧播放:
    CAKeyframeAnimation、UIImageView、定时器类(CADisplayLink、NSTimer、dispatch_source_t)+切换关键帧逻辑
  3. 供所有班帧播放了的风波: CAAnimationDelegate、CATransaction
    CompletionBlock、定时器类+回调触发逻辑

重组方式多, 比如: CALayer+CAKeyframeAnimation+delegate,
UIImageView+定时器, CALayer+定时器类等等.

俺们事先选定这同仿组合展开实施: CALayer+CAKeyframeAnimation+delegate

// 伪代码
- (void)startAnimation {
    UIImage *frame = [UIImage imageWithContentsOfFile:...];
    NSArray<UIImage *> *frames = @[(id)frame.CGImage, ...];
    CAKeyframeAnimation *keyframeAnim = ...;
    keyframeAnim.contents = frames;
    ...
    keyframeAnim.delegate = self;
    [xxx.layer addAnimation:keyframeAnim forKey:@"xxx"];
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    // 触发动画播放结束(全部播放完、中途结束)回调
    ...
}

只要这里你已下载了Demo, 可以打开Debug
Navigator(cmd+6)简单翻看内存增长要留意Xcode
Instrument-Allocations中VM:ImageIO_PNG_Data同等件,
就会见看到有内存增长波峰. 而且序列帧图片越多, 波峰越明显.

这就是说其他方案是否出现了一致之题材吗? 是的, 其他方案一样会这样,
换成UIImageView自带的animationImages来开序列帧播放或是其他组成措施,
也起内存激增的情况.

于CADisplayLink的回调中赢得两赖屏幕刷新的间隔时间,
通过持续的增长间隔时间来判定究竟的流年是不是业已满足下同样帧的播音时刻,
如果大于下同样帧的播报时刻就是可替换为下一样帧图片了,
直至最后一摆放关键帧也播放完成.

2.2 了解图片加载

在我们为懂是什么导致内存激增前, 我们先了解一下图形由磁盘加载,
到写副内存, 最后显示到屏幕及各自都产生了呀. 大致分成如下步骤:

  1. 呢磁盘中之图形创建映射
  2. IO操作读取图片数据流
  3. 图表解码位图拷贝, 写副内存
  4. 硬件绘制渲染到屏幕

举个例子, 我们要当1秒内广播完毕一个涵盖5摆放关键帧图片的卡通片,
每张图纸的停留时间、切换时要下图2.3.2.a所示.
所以第0秒的上就开展示第一摆放关键帧, 直到1.0秒即一阵子常常, 动画播放结束.

2.2.1 映射文件

当我们透过[UIImage imageWithContentsOfFile:]自打磁盘加载图片数据流,
实际上不过是吧者图片创建了一个文件映射数据,
图片文件既无真的让加载到内存,
更不曾为解码成位图的款型而供应Core
Animation传递给底层硬件进行渲染, 故此时内存并无会见显增多,
也无会见油然而生因解码操作导致CPU使用多的情形.
但自从网络下充斥图片数不带有在内.

简言之提及一下炫耀文件:

A mapped file uses virtual memory techniques to avoid copying
pages of the file into memory until they are actually needed.

直译就是一个映射文件指虚拟内存技术来避免当他俩还并未真正使用及常虽为拷贝到内存中.

脚来平等组比说明一下:

本着照组一

- (void)test1 {
    UIImage *frame = [UIImage imageWithContentsOfFile:filePath];
    // 确保超出局部作用域后, 依旧保持对这个Image对象的强引用
    self.frame = frame;
}
// 待上方函数执行完后, 再查看内存使用情况

对照组二

- (void)test2 {
    UIImage *frame = [UIImage imageWithContentsOfFile:filePath];
    self.imageview.image = frame;
}

俺们好窥见相比组二的内存占用明显较对照组同样如果多.
即由此imageWithContentsOfFile:创建的UIImage对象后, 内存并没有明显增强,
等我们将欠UIImage对象赋值给UIImageView的image属性后的某个时刻,
内存才出现鲜明增长.

此间又留几个问题:

  1. 我们都理解imageWithName:办法加载的图, 会被系统缓存,
    那么首先不好通过该方式开展如达到点滴独比组的尝试, 结果如何为?
  2. 通过imageWithName:主意第2、3..n坏加载同名图片时,
    加载的图纸数流会不见面再次为解码? 期间CPU占用起无发多?
  3. 品尝把创建的UIImage对象桥接赋值给CALayer的contents属性, 结果如何?

除此以外, 如果还需更优化,
我们可入图片异步解码、图片预加载逻辑等方案.

2.2.2 浅谈CALayer的隐式动画与工作

自打达亦然省吃,
我们发现当给UIImageView的image属性或CALayer的contents属性赋值Image对象后底之一说话,
内存和CPU占用才会出现鲜明变化. 那是以每一样浅Runloop循环, Core
Animation都见面在那开创造一个动画事务,
在本次Runloop结束时才去执行有添加到拖欠事情里的保有动画操作.
此刻图才给解码加载入内存,
图片数会被解码为渲染可用的bitmap数据.
一些有关细节而圈我其他一样首分享.

浅谈CALayer隐式动画以及工作

  • 异步图片解码, 图片解码是同等宗于耗时、比较占CPU的操作,
    对于未解码的图样, 系统一般会于主线程对该展开解码,
    所以可以经过当异步线程进行图片强制解压缩, 从而不占UI线程.
    关于图片解码的详情, 强烈推荐座谈 iOS
    中图纸的铲除压缩.

  • 图表预加载, 这个就算是以进一步节约上下文切换时,
    即前后两张图纸切换的时间. 就是如完成当及一样帧图片播放完时,
    我们毫不等下同样摆图解码完成后再行展开图片的切换,
    而是可以一直打已经解码图片的缓存队列中取出直接开展切换.
    预加载我个人认为其实要就是阈值的尽美妙选择,
    可参照预加载与智能预加载一文.

2.3 解决内存激增问题

时我们面临的问题是无论用何种实现方案, 在履行序列帧动画时,
所有图片都见面吃解码成为位图并载入内存中.

上图:

2.3.1 解压后的图纸所占内存大小

图表解码后底格式为位图形式.
位图是由于同组像素(pixel)组成的, 每一个像素就意味着图片被的一个点.
比如大规模的JPEG, 以及PNG格式的图样文件还是位图图片.

我们尚需理解, JPEG和PNG图片实际上还是千篇一律种植编码/压缩后的各类图格式,
它们是免可知一直用来图片渲染的, 所以得先对该缩减的数量进行解码/解压缩操作.

那么相同摆放解压后的各项图其所占用内存大小怎么算呢?

这边设我们出雷同张32个之PNG格式图片, 其像素格式为RGBA四部分构成,
每有占用8号, 该图片尺寸为160px * 320px.

32位的图片意味着其每个像素占32位, 即4个字节.
又根据图片尺寸计算出总像素数量为 160*320 个像素.
所以该图片解码后所占内存大小就为 像素总数 * 单位像素的字节数
即 (160*320) * 4 / 1024 = 200 KB.

用可想而知, 假设公海赌船网址一个队列帧动画出80摆放图纸, 200 * 80 / 1024 = 15.625
mb, 就会占用15mb的内存. 序列帧图片越多, 占用内存越大!

公海赌船网址 1

2.3.2 解决方案

那有什么办法好免为? 可也老是播放到哪一样幅时就去加载那一帧之图片,
即每次仅加载一摆放图纸及内存中. 这样当播放到下一样摆设图片时,
上平等摆图纸都无外引用, 系统本来会对那个进行释放.

立就算是不过简单易行有效之平等效方案.
但是我们鞭长莫及借助CAAnimation及其派生类CAKeyframeAnimation来促成即无异方案,
因为有着的图形都见面解码导致占用大量的内存.

然我们得以经过CADisplayLink来贯彻该方案,
选CADisplayLink的原由是其比NSTimer精度要大多,
正常状态下CADisplayLink的回调会在屏幕每次刷新时触发,
即一般1/60秒触发一样蹩脚, 适合用来做UI的重绘,
因此可以由此它来周期性的替换关键帧图片, 从而达到播放动画的法力.
那么具体怎么开啊?

在CADisplayLink的回调中拿走两浅屏幕刷新的间隔时间,
通过持续的增长间隔时间来判定究竟的日子是不是就满足下一致轴的播报时刻,
如果大于下同样幅的播音时刻就是得替换为下一致轴图片了,
直至最后一摆放关键帧也播放完成.

选个例子, 我们若在1秒内广播完毕一个饱含5布置关键帧图片的动画,
每张图纸的停留时间、切换时若是下图2.3.2.a所示.
所以第0秒的时段就是从头显得第一布置关键帧, 直到1.0秒即一刻常, 动画播放结束.

图 2.3.2.a

除此以外, 如果还欲更加优化,
我们可加入图片异步解码、图片预加载逻辑等方案.

  • 异步图片解码, 图片解码是一律件于耗时、比较占CPU的操作,
    对于非解码的图样, 系统一般会以主线程对该展开解码,
    所以可以由此当异步线程进行图纸强制解压缩, 从而非占UI线程.
    关于图片解码的详情, 强烈推荐座谈 iOS
    中图纸的铲除压缩.

  • 图形预加载, 这个就是是为着更节约上下文切换时,
    即前后两布置图切换的时间. 就是要是成功当及同样轴图片播放完时,
    我们绝不等下一样摆放图片解码完成后重新开展图纸的切换,
    而是可以直接由曾解码图片的休养生息存队列中取出直接进行切换.
    预加载我个人觉得实在主要就是是阈值的极精美选择,
    可参看预加载与智能预加载一文.

  • 字节对齐(byte alignment)对Core
    Animation性能的影响

Untitled.gif

其三、序列帧动画引擎源代码和Demo

FXAnimationEngine –
Github跳转

针对该Demo近期会晤另外起一温婉特别介绍, 此处占坑, 等待跳反链接

本人优化后底demo地址:https://github.com/tc1993/keyFrameAnimation

季、礼物资源下载策略和资源目录结构

4.1 礼物资源下载策略

4.1.1 两种植艺术较

方式 基本思路 优点 缺点
整包更新 所有的动画资源按目录结构进行压缩, 客户端通过比较资源包版本号发现有更新后, 仅需下载一个资源包压缩文件, 并进行解压替换即可 简单易实现, 客户端每次仅需下载一个资源包 随着资源包逐渐增大, 下载及解压时间也会延长, 从而直接影响用户体验; 即使是仅是资源中的某个图片发生改变, 客户端都要重新下载整个资源包, 容错率低且浪费流量
增量更新 每个动画资源单独压缩并上传CDN, 若客户端发现资源版本号有变化, 再对服务器下发的资源列表跟本地资源列表求差集运算从而得出增量, 单个动画资源的下载地址或者md5可作为唯一标识进行比较. 得出增量后, 客户端再对每个增量资源包进行下载, 每下载完一个即可"投入使用" 不怕资源变更频繁; 仅需下载有新增或有变更的资源包, 更节省时间以及流量; 逻辑略复杂于整包更新, 比如下载中途用户把应杀掉, 下次需要找出未更新完的增量资源并继续下载

4.1.2 资源创新流程

为对上家公司的代码保密, 此处不达到具体代码

咱们以达标同有点节吃提及的点滴种更新方式,
它们要的不同的就算在”资源创新”这无异步骤

图 4.1.2.a 整包更新的流程图

整包更新流程图.png

图 4.1.2.b 增量更新的流程图

增量更新流程图.png

不理解诸位发现个别只流程共同之处没? 它们还要检测资源版本号大小,
包括游戏补丁、热还补丁这无异手续都必不可少. 相比于上丁类的,
资源创新不用太考虑灰度发布、回滚机制当题材, 但还是仍然用留意资源核对,
内部测试, 以及日志监控等维持,
我记忆在前任公司就撞了有些地方下载下来的资源包有问题,
所以不管是CDN的问题或者资源本身产生问题, 前端都急需吗极深之事态做好打算,
这才是万咸的策.

援我上寒店, 我老大兼mentor, 达文哥, 告诫的同一词箴言

并非相信后台下发的数码还是是的

盖意思这样, 原词没坐下, 立刻词话没不是赖后台同学十分,
或者甩锅给后台
, 而是要prepare for the worst.

内外端测试都是一家人, 遇到问题我们先行瞧是无是友善问题,
不要互相甩锅..本是同根生相煎何太匆忙, 如果有题目虽同样片搓一间断,
一间断好就还来同样刹车

4.2 资源目录结构设计

无谁直播平台, 每个礼物都见面相应一个逻辑id,
我们得以经过人事的id作为该赠品的资源目录名,
然后在该目录外当去划分不同门类的图片子目录, 如下所示

- 10000             // 一级目录, 礼物id
    - - gift        // 二级目录, 小礼物序列帧图片
    - - giftlist    // 二级目录, 礼物列表序列帧图片 
    - - giftanim    // 二级目录, 大动画序列帧图片

及时就是中的等同栽设计, 也有的平台会用如下形式, 所以主要要看需求使定

- gift
    - - 10000
- giftlist
    - - 10000
- giftanim
    - - 10000   

除此以外, 有的平台尚见面动id_version, 即礼物id+礼物版本的样式来定名,
这样好便宜配置使后台可灵活下发给前端具体而失去播放哪个动画的某部版本了

- 10000_11  // id为10000, 版本为11的礼物资源目录
- 10000_12

相关文章