WeTest 导读


现在app越来越炫,动不动就搞点动画,复杂的动画用原生实现起来挺复杂,如是就搞起gif播放动画的形式,节省开发成本。
 

背 景

设计同学准备给一个png序列,开发读取png序列,一帧一帧的播放出来,实现一个动画的效果。

为什么不直接使用gif,github上有好的开源库可以直接播放gif的,为嘛?大部分原因还是要回答,项目需求决定。

实现思路:

1、比较偷懒的方式,将设计同学给的png序列直接放到一个 animation-list中,就像这样子:

然后直接,放在设置为一个ImageView就可以了

那么,真的就可以了吗?答案是,可以,也不可以,因此最终不可以~~(有点绕。。。)

设计同学给了一个90多张png的序列,于是oom的发生,真是悲剧啊,这么简单的方案,结果却是这么华丽的被抛弃了。

2、使用一个线程来读取PNG序列,另外一个线程去播放读取出来的PNG序列,那么有一些问题我们要去面对:

a、一个线程来读,一个线程写,读PNG的线程写,播PNG的线程读,哎呀,有点拗口~~,不过很显然,这是一个《生产者-消费者模型》,那么问题是使用什么存放读取好的bitmap呢,使用BlockingQueue 吧,为什么要使用BlockingQueue,如果不懂,请点击这里,还能不能使用别的,当然,有,而且还不止一个,感兴趣可以去这个包下java.util.concurrent探索下。

b、不是怕OOM吗?那么,这个方案是否可以解决OOM呢?但是显然是肯定的了。为什么这么说,都到了这种粒度了,OOM当然是可以解决。

b1、首先,我们可以拿到当前的最大内存Runtime.getRuntime().maxMemory(),和当前的可用内存Runtime.getRuntime().freeMemory();

因此,结合BitmapFactory.Options,的这个inJustDecodeBounds属性,你完全可以判断是否还有足够的内存加载更多的bitmap。

b2、其次,维护一个currentSize,记录解析到内存测bitmap占用的内存,每读一张,currentSize+读出来的bitmap占用的内存,currentSize显然是变动的,播放完的bitmap请补上一刀,currentSize - 刚刚播放完的bitmap。

那么,整个过程似乎可以用这个图来清晰的表达了:

以为这样就结束了,那你就TOO YOUNG TO SIMPLE 了,是否还能优化?你猜应该是可以吧!

我猜也是可以的,不难发现消费者的消费能力实在太强,读取PNG的线程太不给力,读的太慢了,播放总是等待读新的bitmap出来已供展示。那么?肿么办?

多个线程去读啊!

嗯,似乎可以改进成这样,对吗?

这里,可能有多个读取PNG的线程,一旦引入了多线程,你就会体会到问题会变得复杂多了!

这里,你要控制,当前读取进度到了哪里,因为是多线程,所以,你之间那个简单的int currentLoad 已经不能用了,否则,三个线程读同一张png可能会被你不巧碰到,那么怎么办,使用AtomicInteger,OK,这个问题好像被你解决了,此时,你保证了,所有png被不重复加载完毕!

然而,一个更加头疼的问题还要你去面对,注意,gif是有播放顺序的,然而,你把BlockingQuene做成了这么一个序列:

同学,这样好吗?显然不能接受。那么,如何保证塞入到BlockingQuene中的bitmap是按照png序列的顺序呢?

很显然要做到这一点,就需要将png的序号带入到读取线程中。读取线程读取完毕之后,去问一个manger,大哥,有比我小的读取线程还没有提交他拿到的bitmap吗?大哥告诉你还有,那对不起,你乖乖等一会吧,wait(关键字),对么?如果大哥告诉你没有,你丫就是序号最小的那个哦,那你就把bitmap交给BlockingQuene吧,然而自己就完成光荣使命了。

可问题是,如果你在wait,谁来叫醒你呢?大哥说,他来notify,大哥收到最小的序号的提交的bitmap,等等,(上面说错了,最小的需要把bitmap交给大哥来提交,),将bitmap交给BlockingQuene,然后大哥此时通知所有读取线程的小弟们,大伙赶紧来交作业了,如是此时你单身10年的左手终于抢到了“锁”,如是,你把你的作业bitmap交给了大哥了。

图,我就不画了,脑补也能补出来,不是吗?

不满足锁,可以优化成无锁,大哥可以维护一个序列,1对应的座位只能1提交过来,2对应的只能2提过来,维护一个已交给BlockingQuene位置的游标,有好多种情况,我们用绿色的代表已交给大哥的任务好吗?

如是,这种情况表达0123已经提交给BlockingQuene,5先完成了,然后3完成了,4没完成,此时大哥会吧3提交给BlockingQuene对吗?显然是,情况还有很多,,可以自己脑补一下,总之,这么做,读取线程只要读取完毕,把作业交给大哥就好,不用等待大哥说你是最小的,才让你提交,是吗?

这样就OK了吗?

如果说是,那你还是TOO YOUNG TOO SIMPLE!

万万没想到,之前单个线程读的时候,加载一张PNG耗时才220ms左右,(测试使用模拟器),真机华为mate8略快。

然而,使用多线程读的时候,加载一张PNG居然耗时1100ms左右,开了4个读线程。。,真是醉了。

线程开的有点多?那个2个试试???400ms左右!!!

OH,no,回过头来想想,其实,瓶颈在读没有错,但是读的瓶颈在手机存储卡上。。。或许还有其他因素。

3、不死心,继续思考,单个线程读取png的情况下,是否有可能提高读取效率?

 

先把问题放一放,假如真的找不到好的办法,至少要保证内存占用方面,流畅性方面先,看下内存图谱吧,不看不要紧,一看,就醉了:

细心的同学应该看到了锯齿了,这GC,太酸爽了吧,分析一下,我们没播放完一帧,就将bitmap给回收了(recycle)了。结果就导致了这种图的出现,但是又不能不recycler掉,随着bitmap内存占用不断增加,OOM势必难以避免。

那么,既然释放也不是,不释放也不是,那么,可以不可以将这个要释放的bitmap继续拿过来用呢?

什么意思?

如果要释放的bitmap的那块内存,能够直接用来加载新的png,那该多好啊,那么,是否有这个可能呢?问了下google,他给了我这么一个答案:

https://developer.android.com/training/displaying-bitmaps/manage-memory.html#recycle

options有这么一个参数 ,可以重用一个bitmap的内存去存放解析出另外一个新的bitmap,但是有一定的要求:

4.4以上,只需要old bitmap字节数比将要加载的bitmap所需的字节数大,但是低于4.4,要满足和待加载bitmap长宽像素一致即可 (更加苛刻)。

而我们的png序列,每张图片都是一样大小,显然,符合这个所有特性(长宽一致)。

如是,有多了集合去存储即将释放的bitmap,用来重用。

测试一下:

锯齿果断消失了,而且,似乎还得到一个额外的奖励!

加载速度提升了

分析,可能是因为bitmap内存的重用,使得加载新bitmap的时候不用重新分配内存,节省了一定的时间。

最后看看丝丝顺滑的效果吧

 

 


针对手游的性能优化,腾讯WeTest平台的Cube工具提供了基本所有相关指标的检测,为手游进行最高效和准确的测试服务,不断改善玩家的体验。目前功能还在免费开放中。欢迎立即体验!

帮助中心:http://wetest.qq.com/help/documentation/10096.html

如果对使用当中有任何疑问,欢迎联系腾讯WeTest企业qq:800024531

通过三次优化,我将gif加载优化了16.9%的更多相关文章

  1. Python3从零开始爬取今日头条的新闻【三、滚动到底自动加载】

    Python3从零开始爬取今日头条的新闻[一.开发环境搭建] Python3从零开始爬取今日头条的新闻[二.首页热点新闻抓取] Python3从零开始爬取今日头条的新闻[三.滚动到底自动加载] Pyt ...

  2. vue项目首屏加载优化实战

    问题 单页面应用的一个问题就是首页加载东西过多,加载时间过长.特别在移动端,单页面应用的首屏加载优化更是绕不开的话题.下面我会写出我在项目中做的一些优化,希望大家能够相互讨论,共同进步. 我的项目vu ...

  3. vue加载优化策略

    vue.js是一个比较流行的前端框架,与react.js.angular.js相比来说,vue.js入手曲线更加流畅,不管掌握多少都可以快速上手.但是单页面应用也都有其弊病,有时候首屏加载慢的让人捏舌 ...

  4. unity3d 加载优化建议 总结 from 侑虎科技

    第一部分 我们对于纹理资源的加载建议如下: 1.严格控制RGBA32和ARGB32纹理的使用,在保证视觉效果的前提下,尽可能采用“够用就好”的原则,降低纹理资源的分辨率,以及使用硬件支持的纹理格式. ...

  5. Vue 网站首页加载优化

    Vue 网站首页加载优化 本篇主要讲解 Vue项目打包后 vendor.js 文件很大 如何对它进行优化 以及开启Vue的压缩 和 nginx gzip 压缩的使用, 其他就是对接口优化等  1. v ...

  6. js资源加载优化

    互联网应用或者访问量大的应用,对js的加载优化是不可少的.下面记录几种优化方法 CDN  + 浏览器缓存 CDN(content delivery network)内容分发网络, 最传统的优化方式.其 ...

  7. Multidex(二)之Dex预加载优化

    Multidex(二)之Dex预加载优化 https://www.jianshu.com/p/2891599511ff

  8. react 首屏加载优化

    react 首屏加载优化,原本是在入口HTML文件中加载loading动画,但是部署在测试环境上的时候一直无法显示loading的部分,也是奇怪了,我们测试环境的部署一直跟本地的都不太一样,内外网的转 ...

  9. Vue SPA 首屏加载优化实践

    写在前面 本文记录笔者在Vue SPA项目首屏加载优化过程中遇到的一些坑及优化方案! 我们以 vue-cli 工具为例,使用 vue-router 搭建SPA应用,UI框架选用 element-ui ...

随机推荐

  1. 如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发

    阅读目录 前言 单元测试 纠正错误,重新出发 结语 一.前言 实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发 ...

  2. IE的F12开发人员工具不显示问题

    按下F12之后,开发人员工具在桌面上看不到,但是任务栏里有显示.将鼠标放在任务栏的开发人员工具上,出现一片透明的区域,选中之后却出不来.将鼠标移动到开发人员工具的缩略图上,右键-最大化,工具就全屏出现 ...

  3. 【微框架】之一:从零开始,轻松搞定SpringCloud微框架系列--开山篇(spring boot 小demo)

    Spring顶级框架有众多,那么接下的篇幅,我将重点讲解SpringCloud微框架的实现 Spring 顶级项目,包含众多,我们重点学习一下,SpringCloud项目以及SpringBoot项目 ...

  4. 【接口开发】浅谈 SOAP Webserver 与 Restful Webserver 区别

    接口,强大,简单,交互,跨越平台 下面简单阐述这两大接口思想 一 REST: REST是一种架构风格,其核心是面向资源,REST专门针对网络应用设计和开发方式,以降低开发的复杂性,提高系统的可伸缩性. ...

  5. Python标准模块--Unicode

    1 模块简介 Python 3中最大的变化之一就是删除了Unicode类型.在Python 2中,有str类型和unicode类型,例如, Python 2.7.6 (default, Oct 26 ...

  6. 修改eclipse皮肤

    习惯了vim黑色背景的程序猿们想必用eclipse时会倍感的不适应吧,不过没关系,因为eclipse的皮肤是可以自己定制的! 下面是我电脑上的eclipse界面,看到这个是不是找回了vim的感觉呢? ...

  7. 【项目管理】GitHub使用操作指南

    GitHub使用操作指南 作者:白宁超 2016年10月5日18:51:03> 摘要:GitHub的是版本控制和协作代码托管平台,它可以让你和其他人的项目从任何地方合作.相对于CVS和SVN的联 ...

  8. Oracle手边常用70则脚本知识汇总

    Oracle手边常用70则脚本知识汇总 作者:白宁超 时间:2016年3月4日13:58:36 摘要: 日常使用oracle数据库过程中,常用脚本命令莫不是用户和密码.表空间.多表联合.执行语句等常规 ...

  9. Spring cache简单使用guava cache

    Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...

  10. C#——传值参数(1)

    //我的C#是跟着猛哥(刘铁猛)(算是我的正式老师)<C#语言入门详解>学习的,微信上猛哥也给我讲解了一些不懂得地方,对于我来说简直是一笔巨额财富,难得良师! 这次与大家一起学习C#中的值 ...