I am having lots of images as frames in my resources/drawable folder (let say approx 200). And using this images i want run a animation. The longest animation is of 80Frames. I am successfully able to run the animation on click of the buttons for some, but for some of the animation it is giving me OutOfMemoryError saying that VM can't provide such memory. It is out of VM Budget. I count the size of all of the images its about 10MB. The size of each image is 320x480 in pixels.
I try googling and found that i need to explicitly call the Garbage Collector using System.gc() method. I have done that but still i am getting some time error of memory. Can anyone please kindly help me out in this.
Some Code:-
ImageView img = (ImageView)findViewById(R.id.xxx);
img.setBackgroundResource(R.anim.angry_tail_animation);
AnimationDrawable mailAnimation = (AnimationDrawable) img.getBackground();
MediaPlayer player = MediaPlayer.create(this.getApplicationContext(), R.raw.angry);
if(mailAnimation.isRunning()) {
mailAnimation.stop();
mailAnimation.start();
if (player.isPlaying()) {
player.stop();
player.start();
}
else {
player.start();
}
}
else {
mailAnimation.start();
if (player.isPlaying()) {
player.stop();
player.start();
}
else {
player.start();
}
}
This is the code i have written in on click of a Button.....
Resource file inside res/drawable/anim
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true" >
<item android:drawable="@drawable/cat_angry0000" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0001" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0002" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0003" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0004" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0005" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0006" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0007" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0008" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0009" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0010" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0011" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0012" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0013" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0014" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0015" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0016" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0017" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0018" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0019" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0020" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0021" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0022" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0023" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0024" android:duration="50"/>
<item android:drawable="@drawable/cat_angry0025" android:duration="50"/>
</animation-list>
** The above is the resource file used in setBackgroundResource, same way I am having 10 more file for other different animation. **
Error Log
01-16 22:23:41.594: E/AndroidRuntime(399): FATAL EXCEPTION: main
01-16 22:23:41.594: E/AndroidRuntime(399): java.lang.IllegalStateException: Could not execute method of the activity
01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View$1.onClick(View.java:2144)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View.performClick(View.java:2485)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View$PerformClick.run(View.java:9080)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.os.Handler.handleCallback(Handler.java:587)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.os.Handler.dispatchMessage(Handler.java:92)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.os.Looper.loop(Looper.java:123)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.app.ActivityThread.main(ActivityThread.java:3683)
01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invokeNative(Native Method)
01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invoke(Method.java:507)
01-16 22:23:41.594: E/AndroidRuntime(399): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
01-16 22:23:41.594: E/AndroidRuntime(399): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
01-16 22:23:41.594: E/AndroidRuntime(399): at dalvik.system.NativeStart.main(Native Method)
01-16 22:23:41.594: E/AndroidRuntime(399): Caused by: java.lang.reflect.InvocationTargetException
01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invokeNative(Native Method)
01-16 22:23:41.594: E/AndroidRuntime(399): at java.lang.reflect.Method.invoke(Method.java:507)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View$1.onClick(View.java:2139)
01-16 22:23:41.594: E/AndroidRuntime(399): ... 11 more
01-16 22:23:41.594: E/AndroidRuntime(399): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:460)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.loadDrawable(Resources.java:1709)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.getDrawable(Resources.java:581)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.AnimationDrawable.inflate(AnimationDrawable.java:267)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:787)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.graphics.drawable.Drawable.createFromXml(Drawable.java:728)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.loadDrawable(Resources.java:1694)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.content.res.Resources.getDrawable(Resources.java:581)
01-16 22:23:41.594: E/AndroidRuntime(399): at android.view.View.setBackgroundResource(View.java:7533)
01-16 22:23:41.594: E/AndroidRuntime(399): at talking.cat.CatActivity.middleButtonClicked(CatActivity.java:83)
Same way i have different buttons for different animation... Thanks
I've created an animation class that displays frames based on passed in drawables resources and frames durations.
It is used like this:
and in onCreate:
I solved my outOfMemoryError problem by cutting down the framerate brutally and scaling down the images in gimp. Depending on what you are doing you can probably get away with a lot less fps than you'd expect.
I ported a solution to Xamarin Android and did some improvements.
It works well with orientation changes and specially with images around 300 width and height (the larger the image the longer it takes to load the image, the bigger the flickering).
This is my splash screen class: (this class reads the images from the drawable folder that are named "splash_0001, splash_0002 ...". So no need to name your image resources on an array. Increase the number of frames per second (FPS) to speed up the animation).
It's big problem with the sdk but it can be solved by using threads for concurrently loading the bitmap images instead of loading the entire image at the same time.
I assume that your animation frame images are compressed (PNG or JPG). The compressed size is not useful for calculating how much memory is needed to display them. For that, you need to think about the uncompressed size. This will be the number of pixels (320x480) multiplied by the number of bytes per pixel, which is typically 4 (32 bits). For your images, then, each one will be 614,400 bytes. For the 26-frame animation example you provided, that will require a total of 15,974,400 bytes to hold the raw bitmap data for all the frames, not counting the object overhead.
Looking at the source code for
AnimationDrawable
, it appears to load all of the frames into memory at once, which it would basically have to do for good performance.Whether you can allocate this much memory or not is very system dependent. I would at least recommend trying this on a real device instead of the emulator. You can also try tweaking the emulator's available RAM size, but this is just guessing.
There are ways to use
BitmapFactory.inPreferredConfig
to load bitmaps in a more memory-efficient format like RGB 565 (rather than ARGB 8888). This would save some space, but it still might not be enough.If you can't allocate that much memory at once, you have to consider other options. Most high performance graphics applications (e.g. games) draw their graphics from combinations of smaller graphics (sprites) or 2D or 3D primitives (rectangles, triangles). Drawing a full-screen bitmap for every frame is effectively the same as rendering video; not necessarily the most efficient.
Does the entire content of your animation change with each frame? Another optimization could be to animate only the portion that actually changes, and chop up your bitmaps to account for that.
To summarize, you need to find a way to draw your animation using less memory. There are many options, but it depends a lot on how your animation needs to look.
Similar to other answers, using rxjava: