zoukankan      html  css  js  c++  java
  • 如何解决Android帧动画出现的内存溢出

    这几天在做动画的时候,遇到了一个OOM的问题,特此记录下来。

    普通实现

    实现一个帧动画,最先想到的就是用animation-list将全部图片按顺序放入,并设置时间间隔和播放模式。然后将该drawable设置给ImageView或Progressbar就OK了。

    <?xml version="1.0" encoding="utf-8"?>
    <animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                    android:oneshot="false">
    
        <item android:drawable="@drawable/smile0" android:duration="30"/>
        <item android:drawable="@drawable/smile1" android:duration="30"/>
        <item android:drawable="@drawable/smile2" android:duration="30"/>
        <item android:drawable="@drawable/smile3" android:duration="30"/>
        <item android:drawable="@drawable/smile4" android:duration="30"/>
    </animation-list>
    

    但是如果图片太多了,而且每张图片几百K的情况,就会出现OOM的问题。可以参考Stack Overflow上的这个问题Causing OutOfMemoryError in Frame by Frame Animation in Android

    造成OOM的原因是因为帧动画从xml中读取图片的时候,一次性读取了所有的图片,并设置给ImageView,所以在图片数量过多和图片过大的时候,就会出现OOM。既然知道了原因,那么解决思路也很简单,就是在进行帧动画显示的时候,不要一下子读取所有的图片,而是需要谁就读取谁。

    在github上找到一个例子,tigerjj/FasterAnimationsContainer,具体实现代码如下

    public class FasterAnimationsContainer {
    	private class AnimationFrame{
    		private int mResourceId;
    		private int mDuration;
    		AnimationFrame(int resourceId, int duration){
    			mResourceId = resourceId;
    			mDuration = duration;
    		}
    		public int getResourceId() {
    			return mResourceId;
    		}
    		public int getDuration() {
    			return mDuration;
    		}
    	}
    	private ArrayList<AnimationFrame> mAnimationFrames; // list for all frames of animation
    	private int mIndex; // index of current frame
    
    	private boolean mShouldRun; // true if the animation should continue running. Used to stop the animation
    	private boolean mIsRunning; // true if the animation prevents starting the animation twice
    	private SoftReference<ImageView> mSoftReferenceImageView; // Used to prevent holding ImageView when it should be dead.
    	private Handler mHandler; // Handler to communication with UIThread
    
    	private Bitmap mRecycleBitmap;  //Bitmap can recycle by inBitmap is SDK Version >=11
    
    	// Listeners
    	private OnAnimationStoppedListener mOnAnimationStoppedListener;
    	private OnAnimationFrameChangedListener mOnAnimationFrameChangedListener;
    
    	private FasterAnimationsContainer(ImageView imageView, Context mContext) {
    		this.mContext = mContext;
    		init(imageView, mContext);
    	};
    
    	// single instance procedures
    	private static FasterAnimationsContainer sInstance;
    
    	private Context mContext;
    
    	public static FasterAnimationsContainer getInstance(ImageView imageView, Context mContext) {
    		if (sInstance == null)
    			sInstance = new FasterAnimationsContainer(imageView, mContext);
    		sInstance.mRecycleBitmap = null;
    		return sInstance;
    	}
    
    	/**
    	 * initialize imageview and frames
    	 * @param imageView
    	 * @param mContext
    	 */
    	public void init(ImageView imageView, Context mContext){
    		mAnimationFrames = new ArrayList<AnimationFrame>();
    		mSoftReferenceImageView = new SoftReference<ImageView>(imageView);
    
    		mHandler = new Handler();
    		if(mIsRunning == true){
    			stop();
    		}
    
    		mShouldRun = false;
    		mIsRunning = false;
    
    		mIndex = -1;
    	}
    
    	/**
    	 * add a frame of animation
    	 * @param index index of animation
    	 * @param resId resource id of drawable
    	 * @param interval milliseconds
    	 */
    	public void addFrame(int index, int resId, int interval){
    		mAnimationFrames.add(index, new AnimationFrame(resId, interval));
    	}
    
    	/**
    	 * add a frame of animation
    	 * @param resId resource id of drawable
    	 * @param interval milliseconds
    	 */
    	public void addFrame(int resId, int interval){
    		mAnimationFrames.add(new AnimationFrame(resId, interval));
    	}
    
    	/**
    	 * add all frames of animation
    	 * @param resId resource id of drawable
    	 * @param interval milliseconds
    	 */
    	public void addAllFrames(int resId, int interval){
    		int[] drawableIds = getData(resId);
    		for(int drawableId : drawableIds){
    			mAnimationFrames.add(new AnimationFrame(drawableId, interval));
    		}
    	}
    
    	/**
    	 * 从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;
    	}
    
    	/**
    	 * remove a frame with index
    	 * @param index index of animation
    	 */
    	public void removeFrame(int index){
    		mAnimationFrames.remove(index);
    	}
    
    	/**
    	 * clear all frames
    	 */
    	public void removeAllFrames(){
    		mAnimationFrames.clear();
    	}
    
    	/**
    	 * change a frame of animation
    	 * @param index index of animation
    	 * @param resId resource id of drawable
    	 * @param interval milliseconds
    	 */
    	public void replaceFrame(int index, int resId, int interval){
    		mAnimationFrames.set(index, new AnimationFrame(resId, interval));
    	}
    
    	private AnimationFrame getNext() {
    		mIndex++;
    		if (mIndex >= mAnimationFrames.size())
    			mIndex = 0;
    		return mAnimationFrames.get(mIndex);
    	}
    
    	/**
    	 * Listener of animation to detect stopped
    	 *
    	 */
    	public interface OnAnimationStoppedListener{
    		public void onAnimationStopped();
    	}
    
    	/**
    	 * Listener of animation to get index
    	 *
    	 */
    	public interface OnAnimationFrameChangedListener{
    		public void onAnimationFrameChanged(int index);
    	}
    
    
    	/**
    	 * set a listener for OnAnimationStoppedListener
    	 * @param listener OnAnimationStoppedListener
    	 */
    	public void setOnAnimationStoppedListener(OnAnimationStoppedListener listener){
    		mOnAnimationStoppedListener = listener;
    	}
    
    	/**
    	 * set a listener for OnAnimationFrameChangedListener
    	 * @param listener OnAnimationFrameChangedListener
    	 */
    	public void setOnAnimationFrameChangedListener(OnAnimationFrameChangedListener listener){
    		mOnAnimationFrameChangedListener = listener;
    	}
    
    	/**
    	 * Starts the animation
    	 */
    	public synchronized void start() {
    		mShouldRun = true;
    		if (mIsRunning)
    			return;
    		mHandler.post(new FramesSequenceAnimation());
    	}
    
    	/**
    	 * Stops the animation
    	 */
    	public synchronized void stop() {
    		mShouldRun = false;
    	}
    
    	private class FramesSequenceAnimation implements Runnable{
    
    		@Override
    		public void run() {
    			ImageView imageView = mSoftReferenceImageView.get();
    			if (!mShouldRun || imageView == null) {
    				mIsRunning = false;
    				if (mOnAnimationStoppedListener != null) {
    					mOnAnimationStoppedListener.onAnimationStopped();
    				}
    				return;
    			}
    			mIsRunning = true;
    
    			if (imageView.isShown()) {
    				AnimationFrame frame = getNext();
    				GetImageDrawableTask task = new GetImageDrawableTask(imageView);
    				task.execute(frame.getResourceId());
    				// TODO postDelayed after onPostExecute
    				mHandler.postDelayed(this, frame.getDuration());
    			}
    		}
    	}
    
    	private class GetImageDrawableTask extends AsyncTask<Integer, Void, Drawable> {
    
    		private ImageView mImageView;
    
    		public GetImageDrawableTask(ImageView imageView) {
    			mImageView = imageView;
    		}
    
    		@SuppressLint("NewApi")
    		@Override
    		protected Drawable doInBackground(Integer... params) {
    			if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
    				return mContext.getResources().getDrawable(params[0]);
    			}
    			BitmapFactory.Options options = new BitmapFactory.Options();
    			options.inMutable = true;
    			if (mRecycleBitmap != null)
    				options.inBitmap = mRecycleBitmap;
    			mRecycleBitmap = BitmapFactory.decodeResource(mContext.getResources(), params[0], options);
    			BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(),mRecycleBitmap);
    			return drawable;
    		}
    
    		@Override
    		protected void onPostExecute(Drawable result) {
    			super.onPostExecute(result);
    			if(result!=null) mImageView.setImageDrawable(result);
    			if (mOnAnimationFrameChangedListener != null)
    				mOnAnimationFrameChangedListener.onAnimationFrameChanged(mIndex);
    		}
    
    	}
    
    
  • 相关阅读:
    20172304 2017-2018-2 《程序设计与数据结构》第五周学习总结
    20172304 2017-2018-2 《程序设计与数据结构》第四周学习总结
    20172304 2017-2018《程序设计与数据结构》实验1报告
    20172304 2017-2018-2《程序设计与数据结构》 第3周学习总结
    20172304 2017-2018-2 《程序设计与数据结构》第二周学习总结
    20172304 2017-2018-2《程序结构与程序设计》第一周学习内容总结
    Ubuntu18.04安装教程(一)
    云计算与信息安全第十堂课
    操作系统第十堂课20210510
    云计算与信息安全第八堂课20210427
  • 原文地址:https://www.cnblogs.com/xl-phoenix/p/6922519.html
Copyright © 2011-2022 走看看