1393

I have a ListView with a couple of image buttons on each row. When the user clicks the list row, it launches a new activity. I have had to build my own tabs because of an issue with the camera layout. The activity that gets launched for the result is a map. If I click on my button to launch the image preview (load an image off the SD card) the application returns from the activity back to the ListView activity to the result handler to relaunch my new activity which is nothing more than an image widget.

The image preview on the ListView is being done with the cursor and ListAdapter. This makes it pretty simple, but I am not sure how I can put a resized image (I.e. Smaller bit size not pixel as the src for the image button on the fly. So I just resized the image that came off the phone camera.

The issue is that I get an OutOfMemoryError when it tries to go back and re-launch the 2nd activity.

  • Is there a way I can build the list adapter easily row by row, where I can resize on the fly (bitwise)?

This would be preferable as I also need to make some changes to the properties of the widgets/elements in each row as I am unable to select a row with the touch screen because of the focus issue. (I can use rollerball.)

  • I know I can do an out of band resize and save my image, but that is not really what I want to do, but some sample code for that would be nice.

As soon as I disabled the image on the ListView it worked fine again.

FYI: This is how I was doing it:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME, DBHelper.KEY_ADDRESS, DBHelper.KEY_CITY, DBHelper.KEY_GPSLONG, DBHelper.KEY_GPSLAT, DBHelper.KEY_IMAGEFILENAME + ""}; int[] to = new int[] { R.id.businessname, R.id.address, R.id.city, R.id.gpslong, R.id.gpslat, R.id.imagefilename }; notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to); setListAdapter(notes); 

Where R.id.imagefilename is a ButtonImage.

Here is my LogCat:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process. 01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes 01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.resolveUri(ImageView.java:484) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.setImageURI(ImageView.java:281) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.CursorAdapter.getView(CursorAdapter.java:150) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.obtainView(AbsListView.java:1057) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.makeAndAddView(ListView.java:1616) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.fillSpecific(ListView.java:1177) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.layoutChildren(ListView.java:1454) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.onLayout(AbsListView.java:937) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:922) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:920) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.performTraversals(ViewRoot.java:771) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.handleMessage(ViewRoot.java:1103) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Handler.dispatchMessage(Handler.java:88) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Looper.loop(Looper.java:123) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.app.ActivityThread.main(ActivityThread.java:3742) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invokeNative(Native Method) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invoke(Method.java:515) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497) 01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at dalvik.system.NativeStart.main(Native Method) 01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

I also have a new error when displaying an image:

22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process. 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed 
7
  • 9
    I solved this by avoiding Bitmap.decodeStream or decodeFile and using BitmapFactory.decodeFileDescriptor method. Commented Aug 19, 2011 at 1:56
  • 2
    I Also faced similar issue couple of weeks back and i solved it by scaling down images upto optimal point. I have written complete approach in my blog codingjunkiesforum.wordpress.com/2014/06/12/… and uploaded complete sample project with OOM prone code vs OOM Proof code athttps://github.com/shailendra123/BitmapHandlingDemo Commented Jun 12, 2014 at 10:53
  • 2
    Full solution .. stackoverflow.com/a/24135283/294884 Commented Jul 29, 2014 at 11:29
  • 6
    The accepted answer on this question is being discussed on meta Commented Sep 7, 2015 at 8:44
  • 5
    This happens because of bad android architecture. It should resize images itself like ios and UWP does this. I don't have to do this stuff myself. Android developers get used to that hell and think it works the way it should. Commented Mar 14, 2018 at 11:13

44 Answers 44

1
2
7

It seems the images you have used is very large in size.so some older devices crashes occurs due to heap memory full.In older devices(honey comb or ICS or any low end model devices) try to use android:largeHeap="true" in the manifest file under application tag or reduce the size of the bitmap by using below code.

Bitmap bMap; BitmapFactory.Options options = new BitmapFactory.Options(); options.InSampleSize = 8; bMap= BitmapFactory.DecodeFile(imgFile.AbsolutePath, options); 

you can also give 4 or 12 or 16 to reduce the bitmap size

Sign up to request clarification or add additional context in comments.

Comments

7
BitmapFactory.Options options = new Options(); options.inSampleSize = 32; //img = BitmapFactory.decodeFile(imageids[position], options); Bitmap theImage = BitmapFactory.decodeStream(imageStream,null, options); Bitmap img=theImage.copy(Bitmap.Config.RGB_565,true); theImage.recycle(); theImage = null; System.gc(); //ivlogdp.setImageBitmap(img); Runtime.getRuntime().gc(); 

Comments

5

I tried Thomas Vervest's approach, but it returns a scale of 1 for image size 2592x1944 when IMAGE_MAX_SIZE is 2048.

This version worked for me based on all the other comments provided by others:

private Bitmap decodeFile (File f) { Bitmap b = null; try { // Decode image size BitmapFactory.Options o = new BitmapFactory.Options (); o.inJustDecodeBounds = true; FileInputStream fis = new FileInputStream (f); try { BitmapFactory.decodeStream (fis, null, o); } finally { fis.close (); } int scale = 1; for (int size = Math.max (o.outHeight, o.outWidth); (size>>(scale-1)) > IMAGE_MAX_SIZE; ++scale); // Decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options (); o2.inSampleSize = scale; fis = new FileInputStream (f); try { b = BitmapFactory.decodeStream (fis, null, o2); } finally { fis.close (); } } catch (IOException e) { } return b; } 

Comments

4

use this concept this will help you, After that set the imagebitmap on image view

public static Bitmap convertBitmap(String path) { Bitmap bitmap=null; BitmapFactory.Options bfOptions=new BitmapFactory.Options(); bfOptions.inDither=false; //Disable Dithering mode bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared bfOptions.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future bfOptions.inTempStorage=new byte[32 * 1024]; File file=new File(path); FileInputStream fs=null; try { fs = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { if(fs!=null) { bitmap=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions); } } catch (IOException e) { e.printStackTrace(); } finally{ if(fs!=null) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } } return bitmap; } 

If you want to make a small image from large image with height and width like 60 and 60 and scroll the listview fast then use this concept

public static Bitmap decodeSampledBitmapFromPath(String path, int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; Bitmap bmp = BitmapFactory.decodeFile(path, options); return bmp; } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { if (width > height) { inSampleSize = Math.round((float) height / (float) reqHeight); } else { inSampleSize = Math.round((float) width / (float) reqWidth); } } return inSampleSize; } 

I hope it will help you much.

You can take help from developer site Here

Comments

4

Hi Please visit the link http://developer.android.com/training/displaying-bitmaps/index.html

or just try to retrieve bitmap with the given function

private Bitmap decodeBitmapFile (File f) { Bitmap bitmap = null; try { // Decode image size BitmapFactory.Options o = new BitmapFactory.Options (); o.inJustDecodeBounds = true; FileInputStream fis = new FileInputStream (f); try { BitmapFactory.decodeStream (fis, null, o); } finally { fis.close (); } int scale = 1; for (int size = Math.max (o.outHeight, o.outWidth); (size>>(scale-1)) > IMAGE_MAX_SIZE; ++scale); // Decode with input-stram SampleSize BitmapFactory.Options o2 = new BitmapFactory.Options (); o2.inSampleSize = scale; fis = new FileInputStream (f); try { bitmap = BitmapFactory.decodeStream (fis, null, o2); } finally { fis.close (); } } catch (IOException e) { } return bitmap ; } 

Comments

4

To fix OutOfMemory you should do something like that please try this code

public Bitmap loadBitmap(String URL, BitmapFactory.Options options) { Bitmap bitmap = null; InputStream in = null; options.inSampleSize=4; try { in = OpenHttpConnection(URL); Log.e("In====>", in+""); bitmap = BitmapFactory.decodeStream(in, null, options); Log.e("URL====>", bitmap+""); in.close(); } catch (IOException e1) { } return bitmap; } 

and

try { BitmapFactory.Options bmOptions; bmOptions = new BitmapFactory.Options(); bmOptions.inSampleSize = 1; if(studentImage != null){ galleryThumbnail= loadBitmap(IMAGE_URL+studentImage, bmOptions); } galleryThumbnail=getResizedBitmap(galleryThumbnail, imgEditStudentPhoto.getHeight(), imgEditStudentPhoto.getWidth()); Log.e("Image_Url==>",IMAGE_URL+studentImage+""); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } 

Comments

3

After looking through all the answers, I was surprised to see that no one mentioned the Glide API for handling images. Great library, and abstracts out all the complexity of bitmap management. You can load and resize images quickly with this library and a single line of code.

 Glide.with(this).load(yourImageResource).into(imageview); 

You can get the repository here: https://github.com/bumptech/glide

2 Comments

It does not handle all the scenarios. Glide is not a one stop solution, we are using Glide but still facing many OOM crashes
I wasn't saying it was a one stop solution. I was adding it to the toolbox for those that haven't heard of it.
2

I used Decode File Descriptor which worked for me :

 FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(file); FileDescriptor fd = fileInputStream.getFD(); Bitmap imageBitmap = decodeSampledBitmapFromDescriptor(fd , 612, 816); imageView.setImageBitmap(imageBitmap); } catch (Exception e) { e.printStackTrace(); }finally { if(fileInputStream != null){ try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } 

Code to decode Sampled Bitmap From File Descriptor:

 /** * Decode and sample down a bitmap from a file input stream to the requested width and height. * * @param fileDescriptor The file descriptor to read from * @param reqWidth The requested width of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap * @return A bitmap sampled down from the original with the same aspect ratio and dimensions * that are equal to or greater than the requested width and height */ public static Bitmap decodeSampledBitmapFromDescriptor( FileDescriptor fileDescriptor, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options); } /** * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding * bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates * the closest inSampleSize that will result in the final decoded bitmap having a width and * height equal to or larger than the requested width and height. This implementation does not * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but * results in a larger bitmap which isn't as useful for caching purposes. * * @param options An options object with out* params already populated (run through a decode* * method with inJustDecodeBounds==true * @param reqWidth The requested width of the resulting bitmap * @param reqHeight The requested height of the resulting bitmap * @return The value to be used for inSampleSize */ 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; // This offers some additional logic in case the image has a strange // aspect ratio. For example, a panorama may have a much larger // width than height. In these cases the total pixels might still // end up being too large to fit comfortably in memory, so we should // be more aggressive with sample down the image (=larger inSampleSize). final float totalPixels = width * height; // Anything more than 2x the requested pixels we'll sample down further final float totalReqPixelsCap = reqWidth * reqHeight * 2; while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) { inSampleSize++; } } return inSampleSize; } 

Comments

2

This will get an appropriate bitmap and reduce memory consumption

JAVA

Bitmap bm = null; BitmapFactory.Options bmpOption = new BitmapFactory.Options(); bmpOption.inJustDecodeBounds = true; FileInputStream fis = new FileInputStream(file); BitmapFactory.decodeStream(fis, null, bmpOption); fis.close(); int scale = 1; if (bmpOption.outHeight > IMAGE_MAX_SIZE || bmpOption.outWidth > IMAGE_MAX_SIZE) { scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / (double) Math.max(bmpOption.outHeight, bmpOption.outWidth)) / Math.log(0.5))); } BitmapFactory.Options bmpOption2 = new BitmapFactory.Options(); bmpOption2.inSampleSize = scale; fis = new FileInputStream(file); bm = BitmapFactory.decodeStream(fis, null, bmpOption2); fis.close(); 

Kotlin

val bm:Bitmap = null val bmpOption = BitmapFactory.Options() bmpOption.inJustDecodeBounds = true val fis = FileInputStream(file) BitmapFactory.decodeStream(fis, null, bmpOption) fis.close() val scale = 1 if (bmpOption.outHeight > IMAGE_MAX_SIZE || bmpOption.outWidth > IMAGE_MAX_SIZE) { scale = Math.pow(2.0, Math.ceil((Math.log((IMAGE_MAX_SIZE / Math.max(bmpOption.outHeight, bmpOption.outWidth) as Double)) / Math.log(0.5))).toInt().toDouble()).toInt() } val bmpOption2 = BitmapFactory.Options() bmpOption2.inSampleSize = scale fis = FileInputStream(file) bm = BitmapFactory.decodeStream(fis, null, bmpOption2) fis.close() 

Comments

2

If you are lazy like me, you can start using Picasso library to load images.

Picasso.with(context).load(R.drawable.landing_screen).into(imageView1); Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2); Picasso.with(context).load(new File(...)).into(imageView3); 

Comments

0

Best practices to avoid memory leaks or OOM for bitmap

  1. Do not keep bitmap references for long-lived to a Context / Activity.
  2. If you are using a large bitmap as background or something in your application then don’t pull the full image into the main memory. You can use the insample size property of bitmap to bring the size your screen needs.
  3. Clean bitmap reference once no longer use.

Comments

0

I needed to load a large size image into Bitmap and I used Glide to solve this issue. First check the image size with BitmapFactory.Options using inJustDecodeBounds set to true, then use Glide to get Bitmap object. I used the profiler to check memory usage but I did not see any memory spike like I did when I was using BitmapFactory.decodeFile(). I am writing in c# as I use Xamarin, so need a little tweak to use in Java. Glide library documentation

private Bitmap DecodeFile(File file) { // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); // setting inJustDecodeBounds to true won't load the file into memory, // but gives you the actual file size. options.InJustDecodeBounds = true; BitmapFactory.decodeStream(new FileInputStream(file), null, options); int actualWidth = options.OutWidth; int actualHeight = options.OutHeight; var ratio = (double)actualHeight / actualWidth; // Default to 800 x 600. changed the size whatever you need. var desiredWidth = 800; var desiredHeight = 600; if(actualHeight > actualWidth) { var ratio = (double)actualWidth / actualHeight; var futureTarget = Glide.With(Application.Context) .AsBitmap() .Load(file) .SetDiskCacheStrategy(DiskCacheStrategy.None) .SkipMemoryCache(true) .Submit((int)(desiredWidth * ratio), desiredWidth); bitmap = futureTarget.Get() as Bitmap; } else { var ratio = (double)actualHeight / actualWidth; var futureTarget = Glide.With(Application.Context) .AsBitmap() .Load(file) .SetDiskCacheStrategy(DiskCacheStrategy.None) .SkipMemoryCache(true) .Submit(desiredWidth, (int)(desiredWidth * ratio)); bitmap = futureTarget.Get() as Bitmap; }return bitmap;} 

Comments

-1

Add the following lines to your manifest.xml file:

<application android:hardwareAccelerated="false" android:largeHeap="true"> <activity> </activity> </application> 

1 Comment

please format your code properly, also this doesn't looks like valid xml
-10

After setting an bitmap to imageview, recycle it like this:

bitmap.recycle(); bitmap=null; 

Comments

1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.