0

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.

Prophet
  • 32,350
  • 22
  • 54
  • 79
Andrew Wyld
  • 7,133
  • 7
  • 54
  • 96

2 Answers2

2

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.

Community
  • 1
  • 1
Torid
  • 4,176
  • 1
  • 28
  • 29
  • Ah—so System.gc() probably works for the exact same reason wait(200) does (i.e. they trigger a delay). I mentioned why I can't avoid creating new bitmaps a lot though I have some ideas to make it less frequent; the methods in the article are exactly what I was looking for though. Thanks! – Andrew Wyld Jul 11 '11 at 13:12
  • I've edited the title to reflect the specific Bitmapness of the solution. Thanks! – Andrew Wyld Jul 11 '11 at 13:33
0

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?

Nicklas A.
  • 6,501
  • 7
  • 40
  • 65
  • System.gc() seems to work. We can't edit the bitmap though: the problem is our source bitmaps are HUGE (something like 2000×3000) hence the need to use BitmapRegionDecoder in the first place. There might be a compromise solution (slightly bigger bitmaps than we need would allow fewer reloads for small pans/zooms). – Andrew Wyld Jul 08 '11 at 17:15
  • I would suggest trying not to create bitmaps too often as it is a heavy operation, especially if it's UI related. – Nicklas A. Jul 08 '11 at 17:19
  • Believe me I don't want to. I've edited the title as the answer relates to Bitmap more than general memory. – Andrew Wyld Jul 11 '11 at 13:32