how to relase /free / destroy AnimationDrawable because I got OutOfMemory issue? - android

I am using AnimationDrawable in my all five Activities. I am getting outofMemory error after some time.
problem is related to Virtual Heap Memory and I am finding a way to remove all the earlier/pervious animation when I tap on the new Activity.
I tried some way to :
1) Runtime.getRuntime().gc();
2) activity_name.finish();
3) startGirlBlinking.stop();
iView_cow.setBackgroundDrawable(null);
Logcat :
E/AndroidRuntime(11449): java.lang.OutOfMemoryError
E/AndroidRuntime(11449): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
E/AndroidRuntime(11449): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:577)
E/AndroidRuntime(11449): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:445)
Got help to reslove the Application crash but not for long. I need a valueable solution for this.
How to resove this thing. Plase give me the way to do this.

Extends your AnimationDrawable with this:
public void Recycle() {
for (int i = 0; i < getNumberOfFrames(); ++i){
Drawable frame = getFrame(i);
if (frame instanceof BitmapDrawable) {
((BitmapDrawable)frame).getBitmap().recycle();
}
frame.setCallback(null);
}
setCallback(null);
}

Can you Please Try Like This:
((AnimationDrawable)(someButton.getBackground())).stop();
someButton.setBackgroundDrawable(null);
someButton.setBackgroundResource(R.drawable.animation);

I suppose the problem is that the size of resource you decoded is too big. You should use public static Bitmap decodeResourceStream (Resources res, TypedValue value, InputStream is, Rect pad, BitmapFactory.Options opts) to scale down the resource.

It looks like the bitmap is too big for the device you're running it on. I would suggest doing the following:
Read the documentation here on good practice for handling bitmap - specifically loading large bitmaps efficiently and managing bitmap memory - http://developer.android.com/training/displaying-bitmaps/index.html
Use a graphics program to scale down the bitmap and place it in the according folders - xhdpi/hdpi/mdpi, etc
Look at changing the Bitmap.Config (options.inPreferredConfig) value when decoding the bitmap - http://developer.android.com/reference/android/graphics/Bitmap.Config.html

Related

Android Resources GC Issue

I am using
Drawable drawable = res.getDrawable(id);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);
drawable.setBounds(0,0, width, height);
drawable.draw(canvas);
return load(bitmap, linear);
to load a drawable from a resource id into OpenGL with a given width, and height. (Using
android.opengl.GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
)
The load function does the GL-calls, and calls also bitmap.recycle().
I specify width and height myself, because Android would match the resolution to the screen size, which I don't want.
Now my problem (this part is all working fine):
if I start my app for the first time, from Android Studio, everything works; HOWEVER if I want to restart it, it crashes because of OutOfMemoryError. I am doing the exactly same calls in both cases.
I located the issue to be in the resource management of Android, as you can see in the heap analysis:
my most expensive allocations
My images are way smaller than 9 MB each in raw (512x512, RGBA, so 1 MB).
How can I prevent Android from storing these large byte arrays, which probably are meant as some kind of cache; which however doesn't run on first start after app installation?
I am testing on Android 6.0.1, API Version 23, Galaxy S5.
Implementation of texImage2D looks like this:
public static void texImage2D(int target, int level, int internalformat,
Bitmap bitmap, int border) {
if (bitmap == null) {
throw new NullPointerException("texImage2D can't be used with a null Bitmap");
}
if (bitmap.isRecycled()) {
throw new IllegalArgumentException("bitmap is recycled");
}
if (native_texImage2D(target, level, internalformat, bitmap, -1, border)!=0) {
throw new IllegalArgumentException("invalid Bitmap format");
}
}
It doesn't look like it's recycling anything. Are you sure you are not loading a huge bitmap into memory? Two calls of those are more than enough to guarantee a huge explosion in your app, if not just one (I've seen it happen many times in my app). Remember, restarting your activity does not mean restarting your proccess.
Run the Android Profiler before the first load and check how much memory it takes.
Also, you can cache and reuse bitmaps yourself.
I solved it (myself) by putting the files into the raw folder of the resource directory, and loading them using
fun loadBitmap(res: Resources, rawId: Int): Bitmap {
val inputStream = BufferedInputStream(res.openRawResource(rawId))
return BitmapFactory.decodeStream(inputStream)
}
and then calling
load(bitmap, linear);
and
bitmap.recycle()
like before.
Luckily those all were png/jpeg files, so I didn't need the additional features of the drawables folder. Using this, they'll automatically use their right resolution.
My Java RAM allocation is now back on 25 MB to 35 MB instead of the 110 MB when using the old way :).

When loading bitmap with "BitmapFactory.decodeResource" app crashes

I'm developing a game for little children, which contains different educative exercises.
In one of them I need a picture background, so I'm loading it and scale it with Matrix to fill screen. But sometimes loading picture causes crash.
public DrawThread(SurfaceHolder surfaceHolder, Resources resources, float screen_x_max, float screen_y_max){
this.surfaceHolder = surfaceHolder;
screenWidth=screen_x_max;
screenHeight=screen_y_max;
// picture for bg
picture = BitmapFactory.decodeResource(resources, R.drawable.background);
...
}
At the end of this thread I recycle it and null.
if (this.picture!=null)
{
this.picture.recycle();
this.picture=null;
}
But app still crases. There is error log:
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:494)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:370)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:393)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:419)
at lexu.me.childstudy_lite.DrawThreadFindAnimals2.<init>(findAnimals2.java:271)
at lexu.me.childstudy_lite.findanimals2_view.surfaceCreated(findAnimals2.java:186)
at android.view.SurfaceView.updateWindow(SurfaceView.java:548)
at android.view.SurfaceView.dispatchDraw(SurfaceView.java:353)
at android.view.ViewGroup.drawChild(ViewGroup.java:1737)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1466)
at android.view.ViewGroup.drawChild(ViewGroup.java:1737)
Thanks.
This is a classic problem on Android.
This should provide you with plenty of tips and approaches :
Lazy load of images in ListView
You are getting an Out Of Memory Exception. To avoid this, have a look at this link:
android how to handle out of memory exception
There is a ton of information on the web that will help you with this problem.
I hope this helps.
By down sampling bitmap, you can avoid OME. Have a look at following link. Hope it helps.
Android:Issue Image resolution
Edit: In that link, its taking images from Asset folder. You can modify it according to your need.

Android: bitmap size exceeds VM budget (frame by frame)

I get this error all the time. And as I can see, there are a lot of questions already on stackoverflow.com, but sadly, I don't find any answers which will suit me.
I have 60 PNG images (2,5MB all together) which I would like to put it in animation.
I tried with three different ways.
1
mAnimation = new AnimationDrawable();
mAnimation.addFrame((BitmapDrawable)getResources().getDrawable(R.drawable.yawning_00001), FPS_12);
...
mAnimation.addFrame((BitmapDrawable)getResources().getDrawable(R.drawable.yawning_00063), FPS_12);
mAnimation.start();
2
XML
<animation-list android:oneshot="true">
<item android:drawable="#drawable/yawning_00001" android:duration="83" />
...
<item android:drawable="#drawable/yawning_00063" android:duration="83" />
</animation-list>
Java
ImageView img = (ImageView)findViewById(R.id.animation);
img.setBackgroundResource(R.drawable.yawning);
AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();
frameAnimation.start();
3
With class extending ImageView (I will just show important stuff here)
public void loadAnimation(String prefix, int nframes) {
mBitmapList.clear();
for (int x = 0; x < nframes; x++) {
String zeros = "000";
if (x < 10) {
zeros += "0";
}
String name = prefix + "_" + zeros + x;
Log.d(TAG, "loading animation frame: " + name);
int res_id = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
d = (BitmapDrawable) mContext.getResources().getDrawable(res_id);
mBitmapList.add(d.getBitmap());
}
}
In all cases I get the same error... All some around after 15 picture loads.
E/AndroidRuntime(1591): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
I am begging to wonder if this is frame animations are even possible in Android.
Does anybody maybe have a alternative to frame by frame animation? If yes, please link to any showcase.
You will need to recycle your images in some way because you won't have enough memory ever for 60 images.
You think your images are 2,5 meg all together but this the compressed png version of your files.
If you want to know how much memory you are using with your files when uncompressed in bitmap format in memory just do : width*height*number of images*bytes per pixel....Then you'll know why you crash :D
AnimationDrawable are not meant for that kind of heavy usage. You should start looking at SurfaceViews and then you'll be free to implement whatever memory management method you want to use to display your animation.
http://developer.android.com/reference/android/view/SurfaceView.html
Good luck.
Sometimes, The memory leak come from some where which isn't the line of code show in stack trace. I think you should read below article carefully then check your own code to omit some special issue, such as:
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
Try using the context-application instead of a context-activity
Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance**
Article
EDIT:
You also should check these:
Call recycle() to remove unused bitmap
use sampleSize > 1 to reduce bitmap size.
Bitmap.createBitmap(width,
height, new BitmapFactory.Options().inSampleSize=4)

Is there a way to tell if a Bitmap has been completely disposed on Android?

I am creating a viewer application that calls BitmapRegionDecoder.decodeRegion(Rect, BitmapFactory.Options). I am disposing of the bitmap previously got from decodeRegion before each call:
//this is a function I wrote that gets the rectangle I need
//from the zoom/pan state of a lower-resolution ImageView (page).
//it is bragable.
Rect areaRect = page.getBitmapRegionDecoderRect(areaBitmapRect);
BitmapFactory.Options options = new BitmapFactory.Options();
//area is the ImageView which will get the hi-res bitmap in it.
if(area.getHeight()!=0)
{
//I used Math.round because we are so desperate to save memory!
//The consequent blurring is actually not too bad.
options.inSampleSize = Math.round( ((float) areaRect.height()) / ((float) area.getHeight()) );
}
else
{
options.inSampleSize = Math.round( ((float) areaRect.height()) / ((float) page.getHeight()) );
}
if(options.inSampleSize==0) options.inSampleSize=1;
if(options.inSampleSize>16) options.inSampleSize=16;
options.inPreferredConfig = Bitmap.Config.RGB_565;
if(areaRect.left<0) areaRect.left = 0;
if(areaRect.right>areaBitmapRect.right) areaRect.right = areaBitmapRect.right;
if(areaRect.top<0) areaRect.top = 0;
if(areaRect.bottom>areaBitmapRect.bottom) areaRect.bottom = areaBitmapRect.bottom;
if(areaBitmap!=null)
{
//recycling our garbage ... or are we?
areaBitmap.recycle();
areaBitmap = null;
try
{
//dirty hack
wait(200);
}
catch(Exception x)
{
//something happened.
}
}
//the all-important call
areaBitmap = areaDecoder.decodeRegion(areaRect, options);
area.setImageBitmap(areaBitmap);
I was having problems with the fact that on very quick successive UI events, we were running out of memory. As you can see I have "solved" this with the dirty hack (the thread waits 200ms and gives Android a bit of time to catch up).
I'm not very happy with this, for obvious reasons. Firstly, is my diagnosis (that garbage collection is not finishing before we allocate new memory) correct? Secondly, I tried putting
while(!areaBitmap.isRecycled())
around a counter increment after the recycle() call and the counter stayed at zero. I see the sense in having isRecycled() do this but I need something like an isCompletelyRecycled() method instead. Does Android have anything like this? Thirdly, if I can't get anywhere with this, is there an "available memory" method I can use to tell if my call is going to push us over? I can't find one. It would be nice if Android would say MORE CORE AVAILABLE BUT NONE FOR YOU so I could maybe call my wait loop as a plan B, or eventually try something less intensive instead.
Bitmap memory is allocated in the native heap, so System.gc() will not help. The native heap has its own GC, but clearly it is is not kicking in quickly enough for you - and I am not aware of any way to force it (ie there is not a native analogue of System.gc()).
You could change the structure of your application as Nicklas A suggests to re-use your bitmap.
Or you could use the native heap monitoring mechanisms detailed in BitmapFactory OOM driving me nuts to determine when it is safe to allocate a new bitmap.
Try running System.gc() instead of the sleep.
Actually I'm unsure if this will help as bitmap seems to be mostly native code meaning that it should be released as soon as recycle is called.
To me it seems like creating a new bitmap on every UI event seems like a bad idea, couldn't you just create one bitmap and edit that one?

Bitmap size exceeds VM budget, not understanding why

I've looked all over for "Bitmap size exceeds VM budget" problems, but none of the solutions seem applicable for me. I am not understanding why my program sometimes throws this error because they way I'm using it doesn't seem to cause any possible memory leaks. My stack traces are pointing to the BitmapFactory.decodeResource() method. I've got a background image that I'm using to draw on a Canvas and this is how I've been initializing it:
Bitmap backgroundImage = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
backgroundImage = resizeImage(backgroundImage, w, h);
This is how I've been using it:
canvas.drawBitmap(backgroundImage, 0, 0, paint);
I thought that putting backgroundImage = null in the onDestroy method would help, but that did nothing. There is not other reference to the background image resource in my program except in an XML file, but I don't think that affects it. Could someone explain to me why this is happening and how to fix it?
By the way, there is not screen orientation changes involved in this app.
You need to free the bitmap pixels when you're done with it. You mentioned that you set its value to null, but that only makes it eligible for GC, it does not explicitly tell the VM that you're done with those pixels, and that now is a good time to free them.
Before you set it to null, simply call Bitmap#recycle() on the Bitmap:
protected void onDestroy() {
if (this.backgroundImage != null) {
this.backgroundImage.recycle();
this.backgroundImage = null;
}
}
Additionally, you may be wasting resources in your resizeImage() method, which you did not provide code for. It's much more efficient to do proper down-sampling of the Bitmap at decode-time, rather than loading the full-size Bitmap, and then scaling it down from there.
The general technique is to use the 3-argument version of BitmapFactory.decodeResource(), with BitmapFactory.Options#inJustDecodeBounds for a first-time-pass in order to get the width/height of the Bitmap (although in your case, since it comes from the app's resources, there's no reason you should even have to do that.. but I'll explain it anyway); then determine a proper samplesize based on the target size, and decode the bitmap a second time. That usually results in much less memory usage, especially for very large images (e.g., with inSampleSize set to 2, it decodes the full-size Bitmap, but only allocates enough memory for a Bitmap of half the original size, downscaling the Bitmap in the process).

Categories

Resources