1

I have used the following animation class because i have multiple images to make an animation. After the 7th animation i get OutofMemoryError. androidgraphics.Bitmap.createBitmap

public class AnimationsContainer {
public int FPS = 30;  // animation FPS

// single instance procedures
private static AnimationsContainer mInstance;

private AnimationsContainer() {
};

public static AnimationsContainer getInstance() {
    if (mInstance == null)
        mInstance = new AnimationsContainer();
    return mInstance;
}

// animation progress dialog frames
private int[] mProgressAnimFrames = {};

// animation splash screen frames


/**
 * @param imageView 
 * @return progress dialog animation
 */
public FramesSequenceAnimation createProgressDialogAnim(ImageView imageView) {
    return new FramesSequenceAnimation(imageView, mProgressAnimFrames);
}

/**
 * @param imageView
 * @return splash screen animation
 */
public FramesSequenceAnimation createSplashAnim(ImageView imageView, int[] n) {
    return new FramesSequenceAnimation(imageView, n);
}

/**
 * AnimationPlayer. Plays animation frames sequence in loop
 */
public class FramesSequenceAnimation {
private int[] mFrames; // animation frames
private int mIndex; // current frame
private boolean mShouldRun; // true if the animation should continue running. Used to stop the animation
private boolean mIsRunning; // true if the animation currently running. prevents starting the animation twice
private SoftReference<ImageView> mSoftReferenceImageView; // Used to prevent holding ImageView when it should be dead.
private Handler mHandler;
private int mDelayMillis;
private OnAnimationStoppedListener mOnAnimationStoppedListener;

private Bitmap mBitmap = null;
private BitmapFactory.Options mBitmapOptions;

public FramesSequenceAnimation(ImageView imageView, int[] frames) {
    mHandler = new Handler();
    mFrames = frames;
    mIndex = -1;
    mSoftReferenceImageView = new SoftReference<ImageView>(imageView);
    mShouldRun = false;
    mIsRunning = false;
    mDelayMillis = 50;

    imageView.setImageResource(mFrames[0]);

    // use in place bitmap to save GC work (when animation images are the same size & type)
    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();
        // setup bitmap reuse options. 
        mBitmapOptions.inBitmap = mBitmap;
        mBitmapOptions.inMutable = true;
        mBitmapOptions.inSampleSize = 1;
    }
}

private int getNext() {
    mIndex++;
    if (mIndex == mFrames.length){
        mIndex = mIndex - 1;
    mShouldRun = false;
    }
    return mFrames[mIndex];
}

/**
 * Starts the animation
 */
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.onAnimationStopped();
                }
                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);
}

    /**
     * Stops the animation
     */
    public synchronized void stop() {
        mShouldRun = false;
    }
}
}

Can anyone explain me why and tell me how to fix it? I already added

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

to my manifest file.

Ricardo
  • 9,136
  • 3
  • 29
  • 35
  • are you sure that this decoded bitmap `bitmap = BitmapFactory.decodeResource(imageView.getResources(), imageRes, mBitmapOptions);` always not null? Because once it's null, every loop will load a new image via `imageView.setImageResource(imageRes);`, so there will be OOM exception eventually – Leo May 26 '15 at 01:29
  • Please read the [Android Developer Training for bitmaps](https://developer.android.com/training/displaying-bitmaps/index.html) – StenSoft May 26 '15 at 20:10

1 Answers1

0

It might help to be sure that you are not keeping references to unneeded images. This is happening because you probably have the "standard" memory heap of 32MB or something similar (maybe 64MB or maybe 16MB). If you consider that most images are 5MB or more, it's not surprising you are out of memory.

You can increase the heap size using android:largeHeap="true" like this:

How to increase heap size of an android application?

Community
  • 1
  • 1
Jim
  • 10,172
  • 1
  • 27
  • 36