0

I am very new in android,and trying to put SDcard images in grid view by using Bitmap and BitmapFactory. But it cause the Error like:

ERROR/AndroidRuntime(6137):     java.lang.OutOfMemoryError: bitmap size exceeds VM budget     
ERROR/AndroidRuntime(6137):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
ERROR/AndroidRuntime(6137):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459) 
ERROR/AndroidRuntime(6137):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:271)
WarrenFaith
  • 57,492
  • 25
  • 134
  • 150
Nisha
  • 132
  • 1
  • 7
  • 1
    There are many question on that already answered on SO: http://stackoverflow.com/search?q=bitmap+size+exceeds+VM+budget. – Shivan Dragon Feb 27 '12 at 12:56
  • possible duplicate of [OutOfMemoryError: bitmap size exceeds VM budget :- Android](http://stackoverflow.com/questions/2928002/outofmemoryerror-bitmap-size-exceeds-vm-budget-android) – Shivan Dragon Feb 27 '12 at 12:57

3 Answers3

5

Well ok, apparently this question will get answered the 100th time on SO. Here's the idea:

  • first off depending on the Android version on the device:

    if you're using Android version 2.x and below (any version prior to Honeycomb) the memory taken by your Bitmap instances will NOT be reflected in the amount of free memory you have on the heap as reported by Runtime.getRuntime().xxxMemory() methods. Those instances are placed in memory OUTSIDE the heap. If you want to track down how much memory a Bitmap instance will use you have to manually calculate it as imageWidth*imageHeight*4. This will give you the bytes taken by your image in (off heap) memory. This memory consumption, as well as the on-heap memory consumption must have a total which is below the max memory allocated by Android to your application on a certain device, if not you'll get an OutOfMemory error.

  • the total memory allocated to a process by Android depends greatly on the device and Android version. This can be anywhere between 16 and 48 Megs. On older devices is 16 or 32. On newer ones is 32 or 48. This you have to individually check on each device you want to target. You can do it also at runtime, and use it as a guide as to how much stuff you can put in memory (maybe you want to downsample the images before loading them on a device that allocates 16 Mb of memory to your app)

  • On Android Honeycomb (version 3.x.x) and beyond, a Bitmap instance will use on-heap memory. This makes it easier to track down how much free memory you still have after loading images. Also with these versions of Android, the Bitmap instance will be garbage collected (when possible) automatically. On pre-Honeycomb you have to manyally call

    Bitmap.recycle();

to free up the memory taken by your bitmap instance.

  • When using BitmapFactory to decode images (and create Bitmap instances) you can pass-in options such as to only get the width and height of an image, or to downsample it before decoding. This can help you asses how much memory an image will take BEFORE you actually place it in memory. Check out the docs: http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

  • If you feel adventorous you can trick the memory limitation all together, though this doesn't work on all devices: Create an OpenglSurfaceView, and display your images as textures on quads. You can use an orthogonal projection for simplicity (if you only need to give appearance of 2d). Trick here is that you can load an Image in memory as a bitmap, asign it to an OpenGL texture and the clear out that Bitmap instance. The actual image will still be displayeble from the Texture object, and these objects are not limited by the per-process memory limitation.

Shivan Dragon
  • 15,004
  • 9
  • 62
  • 103
1

Do not copy the image in full quality into your app first. Use the Options class to sample down the quality/size a bit:

ContentResolver cr = getContentResolver();
InputStream is = cr.openInputStream(chosenImageUri);
Options optionSample = new BitmapFactory.Options();
optionSample.inSampleSize = 4; // Or 8 for smaller image
Bitmap bitmap = BitmapFactory.decodeStream(is, null, optionSample);
// Bitmap bitmap = BitmapFactory.decodeFile(filePathString, optionSample);

Try using inSampleSize = 8 if you are creating thumbnail bitmaps.

If you find yourself creating several Bitmap objects, each making some changes to the same image, try using bitmap.recycle(). But recycle() can lead to some runtime errors if your app has some reference to the old bitmaps (can be hard to detect), so be careful using it.

Let me know if it helps.

gorn
  • 8,097
  • 5
  • 37
  • 44
0

Android is more concerned about memory and the BitmapFactory accepts limited size images only.

I think the following will help you to scale image before use.

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;

public class ImageScale 
{

/**
 * Decodes the path of the image to Bitmap Image.
 * @param imagePath : path of the image.
 * @return Bitmap image.
 */

 public Bitmap decodeImage(String imagePath)
 {  
     Bitmap bitmap=null;

     try
     {

         File file=new File(imagePath);
         BitmapFactory.Options o = new BitmapFactory.Options();
         o.inJustDecodeBounds = true;

         BitmapFactory.decodeStream(new FileInputStream(file),null,o);
         final int REQUIRED_SIZE=200;
         int width_tmp=o.outWidth, height_tmp=o.outHeight;

         int scale=1;
         while(true)
         {
             if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
             break;
             width_tmp/=2;
             height_tmp/=2;
             scale*=2;  
         }  

         BitmapFactory.Options options=new BitmapFactory.Options();

         options.inSampleSize=scale;
         bitmap=BitmapFactory.decodeStream(new FileInputStream(file), null, options);

     }  
     catch(Exception e) 
     {  
         bitmap = null;
     }      
     return bitmap; 
 }

 /**
  * Resizes the given Bitmap to Given size.
  * @param bm : Bitmap to resize.
  * @param newHeight : Height to resize.
  * @param newWidth : Width to resize.
  * @return Resized Bitmap.
  */
 public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) 
 {

    Bitmap resizedBitmap = null;
    try
    {
        if(bm!=null)
        {
            int width = bm.getWidth();
            int height = bm.getHeight();
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            // create a matrix for the manipulation
            Matrix matrix = new Matrix();
            // resize the bit map
            matrix.postScale(scaleWidth, scaleHeight);
            // recreate the new Bitmap
            resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);

        }
    }
    catch(Exception e)
    {
        resizedBitmap = null;
    }

    return resizedBitmap;
} 

}

To get image path from URI use this function :

private String decodePath(Uri data)
{        
     Cursor cursor = getContentResolver().query(data, null, null, null, null);
     cursor.moveToFirst(); 
     int idx = cursor.getColumnIndex(ImageColumns.DATA);
     String fileSrc = cursor.getString(idx);   
     return fileSrc;

}
Sadeshkumar Periyasamy
  • 4,848
  • 1
  • 26
  • 31