Reputation: 3204
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
Reputation: 513
edit your code with options.inPurgeable=true and op.inInputShareable=true and do some memory saving methods in your code.
Upvotes: 2
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
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
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
Reputation: 2596
Try adding System.gc()
before every call to loadFromBitmap()
.
Upvotes: 1