Android帧动画/AnimationDrawable导致的OOM/ANR的解决方案

FrameAnimation

如果有播放超多帧动画的需求,直接点击 FrameAnimation 在github查看,基本能满足你的所有需求,就不用往下看了,基本能满足99.99%人的需求。

PS. 此文年久失修,上述代码的具体实现本文已有较大差距,不过整体思路还是可以参考下的。

Android帧动画/AnimationDrawable导致的OOM/ANR的解决方案_第1张图片
值得一个star吗

关于Android帧动画

       当在应用中需要使用帧动画的时候,最先想到的就是Android提供的AnimationDrawable了,但是如果帧动画中如果包含上百帧图片,此时再用AnimationDrawable就不是那么理想了。AnimationDrawable使用一个Drawable数组来存储每一帧的图像,会直接把全部图片加载进内存。随着帧数量的增多,就算性能再强劲的机器也会卡顿、OOM。

使用SurfaceView来实现帧动画的效果

    最近的项目中需要用到大量的帧动画(各种闪瞎24K钛合金狗眼的礼物效果,多的高达200帧),既然AnimationDrawable不行,就想到了两种解决方法。

第一个想到的解决办法就是用openGL来绘制了。

   因为是直播的项目,包含人脸贴图等都是用opengl绘制的,如果用OpenGL绘制一层Texture直接推流还省事。只在主播端处理就行了,但是IOS那边都弄得差不多了,直接原生的不用处理也不会有什么异常什么的。。很尴尬。

第二个就是使用Android自带的surfaceView了

    好吧,第一个不行那就想到Android自带的surfaceView啦。我首先用不同的手机测试了下应用从本地decode一个bitmap的时间(png格式,414*736,大小在30-100k之间),因为帧动画的每帧不会太大,在性能好点的设备上基本保持在10-30ms之间(不推流基本上推流状态下10ms左右,推流状态下20左右),在性能稍差的设备中基本上也不会超过50ms,所以说是没什么大问题的。

在移动设备播放的帧动画一定要尽最大可能的压缩,推荐一个网站,会把图片颜色深度压缩成8位的。TinyPNG

实现思路

整个思路大概如图。
Android帧动画/AnimationDrawable导致的OOM/ANR的解决方案_第2张图片
绘制过程2.png

既然不能完全加载到内存,想到的就是类似视频播放或者视频直播类似的思路。首先定义一个Bitmap的缓冲区,边绘制边加载。首先加载一定数量的帧到Bitmap缓冲区,加载完成后通知SurfaceView开始绘制。SurfaceView绘制一帧完成后通知Bitmap缓冲区加载下一帧,同时将绘制过的一帧的从Bitmap缓冲区移除。一帧绘制完成后,绘制线程根据设置的帧间隔休眠一段时间,休眠完成后开始从Bitmap缓冲区获取下一帧,依此类推,一直循环,直到播放完成或者手动停止。按照这种方式实现起来,发现oom卡顿什么的果然不存在了,内存的使用情况如图。


Android帧动画/AnimationDrawable导致的OOM/ANR的解决方案_第3张图片
内存抖动@2x.png

但是看着这个垃圾桶一个挨一个,这个内存回收情况完全不正常!GC太频繁了。想着应该是这里出现了问题。[图片上传失败...(image-96f387-1512626035688)]
频繁的添加移除bitmap,导致了不算太严重的内存抖动。之所以称之为不算太严重,因为大概400ms一次,一次gc花费2ms左右。不看内存,只看运行效果。真的感觉不出来。但是呢,这样显然也是不行滴。

内存抖动的解决

最常见的解决方法就是对象的复用,创建各种pool。Android也提供了Bitmap的复用方式,在加载bitmap的时候传入一个inBitmap,那么加载的bitmap就会复用原bitmap的内存空间,所以理论上将要复用的bitmap和新加载的bitmap在颜色深度一样的情况下,复用的bitmap宽高要大于新加载的bitmap。50L的桶毕竟最多只能装50L的水。关于inBitmap更多资料可以参考这里,还有这里。(请自备梯子)。 使用起来很简单,大概就是这样

Bitmap mInBitmap;
BitmapFactory.Options mOptions = new BitmapFactory.Options();
mOptions.inMutable = true;
mOptions.inSampleSize = 1;
//mInBitmap不能为null,此处省去赋值
mOptions.inBitmap = mInBitmap;
Bitmap bitmap = BitmapFactory.decodeStream(mAssetManager.open(path), null, mOptions);

然后实现思路就是在这里修改了,把将要删除的哪一帧留下来作为inBitmap。

你可能感兴趣的:(Android帧动画/AnimationDrawable导致的OOM/ANR的解决方案)