anddev
anddev

Reputation: 3204

OutOfMemoryError of Bitmap in Android

I am developing one demo application in which I am playing so many images(Bitmaps). I am using Global Bitmap object for display same image in to more than one Activity. But using that I am geting OutOfMemoryError while I am trying to create Bitmap using Bitmap.createBitmap(...). I tried using this code but still I am getting same error and it is crashing my application and through the OutOfMemoryError.

I am stuck on this. Can anyone has any solution to avoid this.??

Thanks in advance.

I am getting bitmap after cropping the image so that I am using below code for decode the bitmap size.

public static Bitmap loadFromBitmap( Context context, Bitmap b, int maxW, int maxH ) throws IOException {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        Bitmap bitmap = null;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inJustDecodeBounds = true;
        BufferedInputStream stream = null;
        ByteArrayOutputStream outstream = new ByteArrayOutputStream();
        b.compress(CompressFormat.JPEG, 100, outstream);
        InputStream is = new java.io.ByteArrayInputStream(outstream.toByteArray());

        stream = new BufferedInputStream(is, 16384 );
        if ( stream != null ) {
            options.inSampleSize = calculateInSampleSize( options, maxW, maxH );
            stream = null;
            stream = new BufferedInputStream(is, 16384 );
        } else {
            return null;
        }
        options.inDither = false;
        options.inJustDecodeBounds = false;
        options.inPurgeable = true;
        bitmap = BitmapFactory.decodeStream( stream, null, options );
        if ( bitmap != null ) bitmap = BitmapUtils.resizeBitmap( bitmap, maxW, maxH );
        return bitmap;
    }

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

Error:

02-21 15:32:31.160: E/AndroidRuntime(2951): FATAL EXCEPTION: main
02-21 15:32:31.160: E/AndroidRuntime(2951): java.lang.OutOfMemoryError
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:483)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:374)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:404)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.app.Activity.performCreate(Activity.java:4465)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1092)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1924)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1985)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.app.ActivityThread.access$600(ActivityThread.java:127)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1151)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.os.Looper.loop(Looper.java:137)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at android.app.ActivityThread.main(ActivityThread.java:4447)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at java.lang.reflect.Method.invokeNative(Native Method)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at java.lang.reflect.Method.invoke(Method.java:511)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
02-21 15:32:31.160: E/AndroidRuntime(2951):     at dalvik.system.NativeStart.main(Native Method)

Upvotes: 3

Views: 1657

Answers (5)

WSS
WSS

Reputation: 513

edit your code with options.inPurgeable=true and op.inInputShareable=true and do some memory saving methods in your code.

Upvotes: 2

Ben Max Rubinstein
Ben Max Rubinstein

Reputation: 1813

Try to get rid of outstream before you decode, e.g nulling the pointer (closing this specific stream will do nothing due to the type of the stream).

You're doing quite an extensive operation here so we need to be as light as possible...

Your code also lacks closing the input stream (which is not the bug here, but still should be done after you decodeStream), using close().

Try and look into Nostras ImageLoader, as it efficiently handles loading images into specific sized containers, i.e resizing and compressing them, before you will need to handle them. It also supports content uris which will help you all at once.

Upvotes: 4

Michael A.
Michael A.

Reputation: 4218

Ah - the many wonderful battles one may have with Google's implementation of bitmap handling in Android.

I would tend to do some memory profiling anytime I hit the roof with memory - very often (although not always) it is because one is doing something wrong (i.e., unexpectedly memory intensive). Just browsing the code, I am a little bit puzzled by the need to force the code through three streams, just to end up with a bitmap again. I feel as if I'm missing some information for me to make sense of this, but I'd definitely take a look at just how much memory you're allocating in each of those steps if I were you (heap dump -> hprof-conv -> memory analyzer is your friend).

However, I suspect that what you may be looking for is BitmapRegionDecoder. I actually only recently discovered this myself, but I think it does pretty much exactly what you are after - allows you to decode a region from a bitmap (inputstream) without loading the entire bitmap into memory (you can also get the width/height). Only downside is that it is Android 2.3.3+ only, but these days that >90% of the market so that should not be a huge problem. I've done some pretty neat work with this (smooth pan and zoom of 6000x4000 image) - it is definitely memory effective if used right.

[EDIT]

I recently open-source my own dumb implementation using BitmapRegionDecoder which handles pan, fling, and pinch-zoom of a very large bitmap. You can find the code (ASL2.0) at https://github.com/micabyte/android_game

The class you want to look at is BitmapSurfaceRenderer. So far I've tested it with up to 8000x4000 images, and it seems to work very well. When I get the time, I'll put up a simple example app that demonstrates how it is used as well.

Upvotes: 6

Avadhani Y
Avadhani Y

Reputation: 7646

Use the method Bitmap.recycle() before nullfying Bitmap object.

Below is the sample:

 public final void setBitmap(Bitmap bitmap) {
   if (this.myBitmapImage != null) {
      this.myBitmapImage.recycle();
   }
    this.myBitmapImage = bitmap;
 }

And next you can change myBitmapImage without calling System.gc() as follows:

 setMyBitmap(anotherBitmap);
   setMyBitmap(null);

Upvotes: 1

AnujMathur_07
AnujMathur_07

Reputation: 2596

Try adding System.gc() before every call to loadFromBitmap().

Upvotes: 1

Related Questions