Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BitmapFactory.Options.inTempStorage field

Tags:

android

bitmap

I have a custom offline map implemented by drawing Bitmap tiles on Canvas. I'm trying to eliminate object creations to reduce GC runs and therefore make the map scrolling smoother. I see in Allocation Tracker that BitmapFactory.decodeFile(...) always creates byte[16400] object. I thought that setting inTempStorage field of BitmapFactory.Options would change that:

byte[] buffer = new byte[16*1024];
// ...
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.RGB_565;
options.inTempStorage = buffer;
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);

But even with this code I still see decodeFile creating the byte[] array. So what's the problem?

like image 767
fhucho Avatar asked Apr 02 '11 13:04

fhucho


1 Answers

In short, the problem is that when you use BitmapFactory.decodeFile(String, Options) then Android will allocate a 16 kB BufferedInputStream, independently of options.inTempStorage.

To be more elaborate: BitmapFactory.decodeFile(String, Options) is a wrapper around BitmapFactory.decodeStream(InputStream, Rect, Options) that uses a FileInputStream. In the implementation of BitmapFactory.decodeStream(InputStream, Rect, Options), there is this code:

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
    // ...

    // we need mark/reset to work properly
    if (!is.markSupported()) {
        is = new BufferedInputStream(is, 16 * 1024);
    }

    // ...
}

Since FileInputStream's markSupported() returns false, that means that independently of options.inTempStorage, a BufferedInputStream with a 16 kB buffer will be created for you if you use BitmapFactory.decodeFile(String, Options).

To avoid this 16 kB allocation, you could try to use BitmapFactory.decodeStream(InputStream, Rect, Options) directly with an InputStream for which markSupported() returns true.

I can think of two alternatives that could be worth looking into:

  1. Use your own BufferedInputStream with a smaller buffer
  2. Use AssetManager.AssetInputStream as returned by AssetManager.open(...) (see my answer here on how to use it). Its markSupported() returns true.

The first alternative might not help much, you'll still have a byte[] array allocated, but at least it is under your control. The second option might turn out to be the most fruitful, if your circumstances allows you to use this approach.

like image 55
Martin Nordholts Avatar answered Nov 07 '22 23:11

Martin Nordholts