问题描述
需要播放序列帧,帧数特别多的时候会oom
问题分析
源代码每一帧都创建一次bitmap,极度消耗内存
bitmap.recycle并不会立刻回收内存,内存还是会很紧张
问题解决
利用inbitmap,每一帧复用同一片内存区域
//设置Bitmap内存复用
mBitmapOptions.inBitmap = mBitmap;//Bitmap复用内存块,类似对象池,避免不必要的内存
// 初始化bitmap使用该参数bitmap = BitmapFactory.decodeResource(imageView.getResources(), imageRes, mBitmapOptions);
完整代码如下
/*** 循环读取帧---循环播放帧*/public class FramesSequenceAnimation {private int[] mFrames; // 帧数组private int mIndex; // 当前帧private boolean mShouldRun; // 开始/停止播放用private boolean mIsRunning; // 动画是否正在播放,防止重复播放private SoftReference<ImageView> mSoftReferenceImageView; // 软引用ImageView,以便及时释放掉private Handler mHandler;private int mDelayMillis;private OnAnimationStoppedListener mOnAnimationStoppedListener; //播放停止监听private Bitmap mBitmap = null;private BitmapFactory.Options mBitmapOptions;//Bitmap管理类,可有效减少Bitmap的OOM问题public FramesSequenceAnimation(ImageView imageView, int[] frames, int fps) {mHandler = new Handler();mFrames = frames;mIndex = -1;mSoftReferenceImageView = new SoftReference<ImageView>(imageView);mShouldRun = false;mIsRunning = false;mDelayMillis = 1000 / fps;//帧动画时间间隔,毫秒imageView.setImageResource(mFrames[0]);// 当图片大小类型相同时进行复用,避免频繁GCif (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;//解码时返回可变BitmapmBitmapOptions.inSampleSize = 1;//缩放比例}}//循环读取下一帧private int getNext() {mIndex++;if (mIndex >= mFrames.length) {mIndex = 0;}return mFrames[mIndex];}/*** 播放动画,同步锁防止多线程读帧时,数据安全问题*/public synchronized void start() {mShouldRun = true;if (mIsRunning) {return;}Runnable runnable = new Runnable() {@Overridepublic 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 >= 11Bitmap 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);}/*** 停止播放*/public synchronized void stop() {mShouldRun = false;if (mHandler != null) {mHandler.removeCallbacksAndMessages(null);}}/*** 设置停止播放监听** @param listener*/public void setOnAnimStopListener(OnAnimationStoppedListener listener) {this.mOnAnimationStoppedListener = listener;}}