0

Whenever I force portrait mode in onCreate

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

I get an error

What does Portrait mode have to do with OOM VM budget on image scale?

private void scaleFrom(BmpWrap image, Bitmap bmp)
    {
      if (image.bmp != null && image.bmp != bmp) {
        image.bmp.recycle();
      }

      if (mDisplayScale > 0.99999 && mDisplayScale < 1.00001) {
        image.bmp = bmp;
        return;
      }
      int dstWidth = (int)(bmp.getWidth() * mDisplayScale);
      int dstHeight = (int)(bmp.getHeight() * mDisplayScale);
      image.bmp = Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight, true);
    }

    private void resizeBitmaps()
    {

      scaleFrom(mBackground, mBackgroundOrig);
      for (int i = 0; i < mBOrig.length; i++) {
        scaleFrom(mB[i], mBOrig[i]);
      }
      for (int i = 0; i < mBlind.length; i++) {
        scaleFrom(mBlind[i], mBlindOrig[i]);
      }
      for (int i = 0; i < mFrozen.length; i++) {
        scaleFrom(mFrozen[i], mFrozenOrig[i]);
      }
      for (int i = 0; i < mTargeted.length; i++) {
        scaleFrom(mTargeted[i], mTargetedOrig[i]);
      }
      scaleFrom(mBlink, mBlinkOrig);
      scaleFrom(mWon, mWonOrig);
      scaleFrom(mLost, mLostOrig);

      mImagesReady = true;
    }

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Native Method)
at android.graphics.Bitmap.createBitmap(Bitmap.java:498)
at android.graphics.Bitmap.createBitmap(Bitmap.java:465)
at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:370)
at com.company.app.View$hread.scaleFrom(View.java:313)
at com.company.app.View$hread.resizeBitmaps(View.java:337)
at com.company.app.View$hread.setSurfaceSize(View.java:480)
at com.company.app.View.surfaceChanged(View.java:905)
at android.view.SurfaceView.updateWindow(SurfaceView.java:538)
at android.view.SurfaceView.dispatchDraw(SurfaceView.java:339)
at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.ViewGroup.drawChild(ViewGroup.java:1638)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.View.draw(View.java:6745)
at android.widget.FrameLayout.draw(FrameLayout.java:352)
at android.view.ViewGroup.drawChild(ViewGroup.java:1640)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1367)
at android.view.View.draw(View.java:6745)
at android.widget.FrameLayout.draw(FrameLayout.java:352)
at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1891)
at android.view.ViewRoot.draw(ViewRoot.java:1416)
at android.view.ViewRoot.performTraversals(ViewRoot.java:1172)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1736)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:4701)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Kara
  • 6,115
  • 16
  • 50
  • 57
user583739
  • 33
  • 8

2 Answers2

0

Whenever you do an orientation change, the app is reloaded entirely except for static properties. (read here : http://developer.android.com/reference/android/app/Activity.html)

This means that all the bitmap you allocated if they were not static will be reloaded. Since the memory allowed for one app is 16 MB(24 on some devices i hear) and that bitmap are stored in memory as raw bitmap(read not compressed) this create a spike in the memory usage that can lead to OOM

Add to that the fact that the memory used for bitmaps are allocated outside of your heap memory but counted as if part of it so that you can't actually trace that problem even using ddms or MAT.

Be sure to recycle all your bitmaps in your onDestroy method so that they can be garbage collected.

The one thing that finally got me out of this one are these three lines of code(hack) :

System.gc();
System.runFinalization();
System.gc();

Beware though, it impacts performance(about 500 to 750 ms) so it is not suitable for a game with FPS concern but for an app it is perfectly reasonable.

Put them at the very beginning of your createScaledBitmap call.

It worked for me

Edit :

Depending on what you are doing with the bitmap in question you can ask android to open it downsampled, taking less memory. I wrote this function while dealing with this problem. It tries to open the bitmap as big as possible :

    private Bitmap getDownsampledBitmapFromFile(String fileName, int sampleSize) {

        //Try to free up some memory
        System.gc();
        System.runFinalization();
        System.gc();

        BitmapFactory.Options options=new BitmapFactory.Options();//reset object            
        byte[] tempBuffer=new byte[8000]; 
        options.inTempStorage = tempBuffer;
        options.inSampleSize=sampleSize;

        Bitmap downsampledBitmap = null;

        try {
            downsampledBitmap = BitmapFactory.decodeFile(fileNameToUpload, options);
        } catch (OutOfMemoryError e) {
            sampleSize ++;
        }

        return(downsampledBitmap);

    }
Yahel
  • 8,522
  • 2
  • 24
  • 32
  • System.gc(); System.runFinalization(); System.gc(); image.bmp = Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight, true); – user583739 Jan 27 '11 at 04:31
  • Added an edit to my post with a function you might want to try. – Yahel Jan 27 '11 at 07:33
  • What did you set this variable to in the function fileNameToUpload? – user583739 Jan 27 '11 at 09:07
  • This error also only occurs on the AVD emulator not on devices with greater memory. On my person Desire HD I have never had problem but on the AVD I get these errors. Do you know why? – user583739 Jan 27 '11 at 10:38
  • I only gave you this function as a hint on how to use bitmapFactory and BitmapFactory.options to create a bitmap. After that the implementation is yours – Yahel Jan 27 '11 at 12:58
  • Same for me, works on some device, not on others. I guess it depends on a lot of factors(carrier customization, ...) – Yahel Jan 27 '11 at 12:59
  • Yahel it seems to slow my app up greatley as it is a game. Any other work around? – user583739 Jan 28 '11 at 22:05
  • I told you about that in my original answer. If its a game it's not suitable. Have you optimized you bitmaps as much as possible ? If it's a game you shouldn't be scaling bitmaps, this seems inefficient. Tell us more about your game and what your trying to achieve and why you need to scale bitmaps ? – Yahel Jan 29 '11 at 10:07
  • BitmapFactory.Options options = new BitmapFactory.Options(); options.inTempStorage = new byte[16*1024]; bitmapImage = BitmapFactory.decodeFile(path,opt); – user583739 Feb 02 '11 at 11:02
  • Do you know what it seems it only happens to devices with really low memory as my users dont have much of an issue at all. – user583739 Feb 02 '11 at 12:39
  • It was expected since it's a memory problem :) But I thought Android implementation delivered 16 or 24 mb sandboxes depending on the device anyway. So really as long as your app doesn't break those boundaries you should be fine...and we find that we are not :s – Yahel Feb 03 '11 at 13:27
0

The OOM exception is because you are running out of Native heap by recreating bitmaps on the orientation change. See my post at BitmapFactory OOM driving me nuts for background.

One way around this is to (as Yahel says) recycle your bitmaps in onDestroy. Although the bitmap data are in the Native heap, and we found that the code of the form below (in onDestroy) is more effective in getting the heap back.

mBitmap.recycle();
mBitmap = null;
Community
  • 1
  • 1
Torid
  • 4,176
  • 1
  • 28
  • 29