Android AnimationDrawable帧动画OOM问题优化

转自http://blog.csdn.net/wanmeilang123/article/details/53929484


  • 普通实现

实现一个帧动画,最先想到的就是用animation-list将全部图片按顺序放入,并设置时间间隔和播放模式。然后将该drawable设置给ImageView或Progressbar就OK了。 
首先创建帧动画资源文件drawable/anim.xml,oneshot=false为循环播放模式,ture为单次播放;duration为每帧时间间隔,单位毫秒。

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false" >

    <item
        android:drawable="@drawable/img0"
        android:duration="17" />
    <item
        android:drawable="@drawable/img1"
        android:duration="17" />
    <item
        android:drawable="@drawable/img2"
        android:duration="17" />
</animation-list>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然后在代码中找到播放该动画的ImageView,将资源赋给该控件就可以控制动画开始与结束了,是不是超简单。

AnimationDrawable animationDrawable;
if (imageView.getDrawable() == null) {
                     imageView.setImageResource(R.drawable.loading_anim);
                        animationDrawable = (AnimationDrawable) imageView.getDrawable();
}
animationDrawable.start();//开始
animationDrawable.stop();//结束       
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • OOM问题及优化

. 内存溢出咋办

用普通方法实现帧动画用到普通场景是没问题的,如果碰到几十甚至几百帧图片,而且每张图片几百K的情况,呵呵。例如,做一个很炫的闪屏帧动画,要保证高清且动作丝滑,就需要至少几十张高清图片。这时,OOM问题就出来了,闪屏进化成了一闪~ 
别慌,像我这样的菜鸟遇到问题就会找度娘和gayhub,大神们肯定已经有解决方案了。随手一搜就在StackOverflow上找到了解决办法: 
http://stackoverflow.com/questions/8692328/causing-outofmemoryerror-in-frame-by-frame-animation-in-android

. 解决思路 
先分析下普通方法为啥会OOM,从xml中读取到图片id列表后就去硬盘中找这些图片资源,将图片全部读出来后按顺序设置给ImageView,利用视觉暂留效果实现了动画。一次拿出这么多图片,而系统都是以Bitmap位图形式读取的(作为OOM的常客,这锅Bitmap来背);而动画的播放是按顺序来的,大量Bitmap就排好队等待播放然后释放,然而这个排队的地方只有10平米,呵呵~发现问题了吧。 
按照大神的思路,既然来这么多Bitmap,一次却只能临幸一个,那么就翻牌子吧,轮到谁就派个线程去叫谁,bitmap1叫到了得叫上下一位bitmap2做准备,这样更迭效率高一些。为了避免某个bitmap已被叫走了线程白跑一趟的情况,加个Synchronized同步下数据信息,实现代码如下:

public synchronized void start() {
            mShouldRun = true;
            if (mIsRunning)
                return;

            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    ImageView imageView = mSoftReferenceImageView.get();
                    if (!mShouldRun || imageView == null) {
                        mIsRunning = false;
                        if (mOnAnimationStoppedListener != null) {
                            mOnAnimationStoppedListener.AnimationStopped();
                        }
                        return;
                    }

                    mIsRunning = true;
                    //新开线程去读下一帧
                    mHandler.postDelayed(this, mDelayMillis);

                    if (imageView.isShown()) {
                        int imageRes = getNext();
                        if (mBitmap != null) { // so Build.VERSION.SDK_INT >= 11
                            Bitmap bitmap = null;
                            try {
                                bitmap = BitmapFactory.decodeResource(imageView.getResources(), imageRes, mBitmapOptions);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            if (bitmap != null) {
                                imageView.setImageBitmap(bitmap);
                            } else {
                                imageView.setImageResource(imageRes);
                                mBitmap.recycle();
                                mBitmap = null;
                            }
                        } else {
                            imageView.setImageResource(imageRes);
                        }
                    }

                }
            };

            mHandler.post(runnable);
        }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

. 进一步优化 
为了快速读取SD卡中的图片资源,这里用到了Apache的IOUtils。在位图处理上使用了BitmapFactory.Options()相关设置,InBitmap,当图片大小类型相同时,虚拟机就对位图进行内存复用,不再分配新的内存,可以避免不必要的内存分配及GC。

if (Build.VERSION.SDK_INT >= 11) {
                Bitmap bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
                int width = bmp.getWidth();
                int height = bmp.getHeight();
                Bitmap.Config config = bmp.getConfig();
                mBitmap = Bitmap.createBitmap(width, height, config);
                mBitmapOptions = new BitmapFactory.Options();
                //设置Bitmap内存复用
                mBitmapOptions.inBitmap = mBitmap;//Bitmap复用内存块
                mBitmapOptions.inMutable = true;//解码时返回可变Bitmap
                mBitmapOptions.inSampleSize = 1;//缩放比例
            }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 实现过程

将大神的代码整理搬运后,整合成demo到自己GitHub上,具体项目请移步 
https://github.com/VDshixiaoming/AnimationTest 
使用很简单:

/**
 * 将帧动画资源id以字符串数组形式写到values/arrays.xml中
 * FPS为每秒播放帧数,FPS = 1/T,(T--每帧间隔时间秒)
 */

AnimationsContainer.FramesSequenceAnimation animation 
        = AnimationsContainer.getInstance(R.array.XXX, FPS).createProgressDialogAnim(imageView);

animation.start();//动画开始
animation.stop();//动画结束
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注意图片资源ID需要以String数组形式放入xml中,然后再利用TypedArray将字符串转为资源ID。如果直接用@drawable/img1这样的形式放入Int数组中,是没法读取到正真的资源ID的。 
从xml中读取资源ID数组代码:

/**
     * 从xml中读取帧数组
     * @param resId
     * @return
     */
    private int[] getData(int resId){
        TypedArray array = mContext.getResources().obtainTypedArray(resId);

        int len = array.length();
        int[] intArray = new int[array.length()];

        for(int i = 0; i < len; i++){
            intArray[i] = array.getResourceId(i, 0);
        }
        array.recycle();
        return intArray;
    }
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 实现效果

实现完了当然迫不及待的到真机上跑跑,测下效果。 
普通帧动画内存使用情况
看图说话,这么明显的坎儿就是应为一次读取图片造成的,分分钟OOM的节奏。再看看优化后的结果: 
优化后帧动画内存使用情况 
是不是很养眼,不过最前面刚进界面时的蜜汁GC我还没弄清除~~ 
最后奉上动图效果,是不是非常丝滑酸爽 
优化后帧动画效果 
Demo地址: 
https://github.com/VDshixiaoming/AnimationTest


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值