Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I NOT get an out of memory exception?

I have a high resolution image (2588*1603) in drawable folder. If I use below code (1) to set it for the imageView I do not get OOM exception and the image assigned as expected:

public class MainActivity extends ActionBarActivity{


    private ImageView mImageView;

    int mImageHeight = 0;
    int mImageWidth  = 0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

      mImageView = (ImageView) findViewById(R.id.imageView);
      mImageView.setScaleType(ScaleType.FIT_CENTER);

      BitmapFactory.Options sizeOption = new BitmapFactory.Options();
      sizeOption.inJustDecodeBounds = true;
      BitmapFactory.decodeResource(getResources(), R.drawable.a, sizeOption);
      mImageHeight = sizeOption.outHeight;
      mImageWidth  = sizeOption.outWidth; 

      mImageView.post(new Runnable() {
          @Override
          public void run() {
              try {
                BitmapRegionDecoder bmpDecoder = BitmapRegionDecoder
                          .newInstance(getResources().openRawResource(R.drawable.a),true);
            Rect rect = new Rect(0,0,mImageWidth, mImageHeight);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            options.inDensity = getResources().getDisplayMetrics().densityDpi;
            Bitmap bmp = bmpDecoder.decodeRegion(rect, options);

            mImageView.setImageBitmap(bmp);  

            } catch (NotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }   
          }
      });

    }
}

Note that rect size is exactly the same as image size.

But If I use other methods like for example 2 or 3 I get OOM.

  2)  mImageView.setBackgroundResource(R.drawable.a);

  3) Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.a);
     mImageView.setImageBitmap(bmp);

What is the difference between 1 and 2,3 ?

(I know how to solve OOM, I just want to know the difference)

like image 605
mmlooloo Avatar asked May 01 '15 14:05

mmlooloo


People also ask

What could be the one reason to get an out of memory error?

Also, this error may be thrown when there is insufficient native memory to support the loading of a Java class. In a rare instance, a java. lang. OutOfMemoryError may be thrown when an excessive amount of time is being spent doing garbage collection and little memory is being freed.


1 Answers

This is the source of BitmapRegionDecoder#decodeRegion:

public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
    checkRecycled("decodeRegion called on recycled region decoder");
    if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
            || rect.bottom > getHeight())
        throw new IllegalArgumentException("rectangle is not inside the image");
    return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
            rect.right - rect.left, rect.bottom - rect.top, options);
}

As you can see, it simply calls a native method. I do not understand enough C++ to see whether the method scales the bitmap down (according to your inDensity flag).

The other two methods use the same native method (nativeDecodeAsset) to get the bitmap.

Number 2 caches the drawable and thus needs more memory.
After lots of operations (checking if the bitmap is already preloaded or cashed and other things), it calls a native method to get the bitmap. Then, it caches the drawable and sets the background image.

Number 3 is pretty straight forward, it calls a native method after a few operations.


Conclusion: For me, it is hard to say which scenario applies here, but it should be one of these two.
  1. Your first attemp scales the bitmap down (the inDensity flag) and thus needs less memory.
  2. All three methods need more or less the same amount of memory, number 2 and 3 just a little bit more. Your image uses ~16MB RAM, which is the maximum heap size on some phones. Number 1 could be under that limit, while the other two are slightly above the threshold.

I suggest you to debug this problem. In your Manifest, set android:largeHeap="true" to get more memory. Then, run your 3 different attemps and log the heap size and the bytes allocated by the bitmap.

long maxMemory = Runtime.getRuntime().maxMemory();
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
long freeMemory = maxMemory - usedMemory;
long bitmapSize = bmp.getAllocationByteCount();

This will give you a better overview.

like image 199
Manuel Allenspach Avatar answered Sep 21 '22 05:09

Manuel Allenspach