前言
作者在项目中遇到了一个有三百多帧的动画,第一想法就是:糟了,Android帧动画肯定用不了了,肯定会OOM,自己也是不死心,用了一下试试果然OOM了QAQ,那就另想办法吧。
于是就去Google,搜到了这么一篇文章:Android帧动画实现,防OOM,比原生动画集节约超过十倍的资源
然后就自己实现了一下:
private fun play(currentFrame: Int) {
runnable.currentFrame = currentFrame
mImageView.postDelayed(runnable, mDuration)
}
inner class FrameRunnable : Runnable {
var currentFrame = 0
override fun run() {
if (mPause) {
mCurrentFrame = currentFrame
return
}
mImageView.setBackgroundResource(mFrameRes[currentFrame])
if (currentFrame == mLastFrame) {
if (mIsRepeat) {
currentFrame = 0
mDuration = 50
play(0)
}
} else {
currentFrame++
play(currentFrame)
}
}
}
复制代码
从代码中可以看出,通过不断地设置背景去实现序列帧动画,程序是不崩了,但是又有一个问题——内存抖动,可以看一下我的运行截图:
要想解决内存抖动,要知道什么是内存抖动,怎么定位内存抖动内存抖动
简而言之,内存抖动是因为在短时间内大量的对象被创建又马上被释放。
有兴趣的同学可以看一下Google官方视频:Android Performance Patterns: Memory Churn and Performance
定位内存抖动
先看一下setBackgroundResource
调用栈(PS:第一次画,哪里不对欢迎指正QAQ):
Bitmap
被大量创建的原因,通过
Android Profiler
也得到了证实。
解决内存抖动
通常我们遇到内存抖动的问题都会想到使用对象池,但是在这里怎样才能复用Bitmap
呢,从上图可以看出这个过程好像没有办法干预,最后决定不使用setBackgroundResource
而是使用setImageBitmap
,这样不就可以从BitmapPool
里取出Bitmap
放进去了吗,可是问题又来了,由于播放的是序列帧每张图都不一样,如何做到复用Bitmap
,所以这个方案个人认为无法实现。
后来,同事说BitmapFactory.Options
有个inBitmap
可以复用Bitmap
,于是又去Google搜索用法, inBitmap
主要就是指的复用内存块,不需要在重新给这个Bitmap
申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。 有兴趣的同学可以看一下Google官方视频:Re-using Bitmaps
最终方案
class FrameAnimation {
private val options: BitmapFactory.Options = BitmapFactory.Options()
constructor() {
...
//这个属性很关键,不加无法复用
options.inMutable = true
options.inBitmap = BitmapFactory.decodeResource(resources, resId, options)
}
inner class FrameRunnable : Runnable {
override fun run() {
val bitmap = BitmapFactory.decodeResource(resources, resId, options)
mImageView.setImageBitmap(bitmap)
}
}
}
复制代码
最后看一下内存情况:
问题可算是解决了,哈哈。