By Chrispix


2009-01-25 11:23:37 8 Comments

I have a list view with a couple of image buttons on each row. When you click 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 list view 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 out of memory error 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 (bit wise)?

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 roller ball.)

  • I know I can do an out of band resize and save of 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 list view 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:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

30 comments

@Deepya 2018-11-01 06:02:33

Add the following lines to your manifest.xml file:

<application

    android:hardwareAccelerated="false"
    android:largeHeap="true">

    <activity>
    </activity>

</application>

@Vaibhav Vishal 2018-11-01 06:26:36

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

@Sanjay Bhalani 2019-06-25 05:33:01

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.

@Naimish Vinchhi 2019-03-08 13:45:00

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()

@Fraggle 2011-08-19 01:55:38

I had this same issue and solved it by avoiding the BitmapFactory.decodeStream or decodeFile functions and instead used BitmapFactory.decodeFileDescriptor

decodeFileDescriptor looks like it calls different native methods than the decodeStream/decodeFile.

Anyways, what worked was this (note that I added some options as some had above, but that's not what made the difference. What is critical is the call to BitmapFactory.decodeFileDescriptor instead of decodeStream or decodeFile):

private void showImage(String path)   {

    Log.i("showImage","loading:"+path);
    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) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;                        
}

I think there is a problem with the native function used in decodeStream/decodeFile. I have confirmed that a different native method is called when using decodeFileDescriptor. Also what I've read is "that Images (Bitmaps) are not allocated in a standard Java way but via native calls; the allocations are done outside of the virtual heap, but are counted against it!"

@PiyushMishra 2011-10-11 12:32:50

same result out of memeory, actually it wont matter which method you are using it depend upon the number of bytes you are holding on to read the data that gives out of memory.

@Jesse 2012-03-27 18:56:47

What if the path is a url?

@vineet 2012-10-31 12:03:07

This code will help to load large bitmap from drawable

public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {

    Context context;

    public BitmapUtilsTask(Context context) {
        this.context = context;
    }

    /**
     * Loads a bitmap from the specified url.
     * 
     * @param url The location of the bitmap asset
     * @return The bitmap, or null if it could not be loaded
     * @throws IOException
     * @throws MalformedURLException
     */
    public Bitmap getBitmap() throws MalformedURLException, IOException {       

        // Get the source image's dimensions
        int desiredWidth = 1000;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;

        // Only scale if the source is big enough. This code is just trying
        // to fit a image into a certain width.
        if (desiredWidth > srcWidth)
            desiredWidth = srcWidth;

        // Calculate the correct inSampleSize/scale value. This helps reduce
        // memory use. It should be a power of 2
        int inSampleSize = 1;
        while (srcWidth / 2 > desiredWidth) {
            srcWidth /= 2;
            srcHeight /= 2;
            inSampleSize *= 2;
        }
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = inSampleSize;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        Bitmap sampledSrcBitmap;

        sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        return sampledSrcBitmap;
    }

    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */
    @Override
    protected Bitmap doInBackground(Object... item) {
        try { 
          return getBitmap();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

@Chrispix 2012-10-31 19:41:12

Nice, think it would be better to use a Loader instead of Async Task?

@Hamzeh Soboh 2018-09-17 12:15:29

How about Bitmap.Config.ARGB_565? If high quality is not critical.

@Mahesh 2012-11-26 05:48:17

Generally android device heap size is only 16MB (varies from device/OS see post Heap Sizes), if you are loading the images and it crosses the size of 16MB , it will throw out of memory exception, instead of using the Bitmap for , loading images from SD card or from resources or even from network try to using getImageUri , loading bitmap require more memory , or you can set bitmap to null if your work done with that bitmap.

@Mahesh 2013-03-13 04:44:12

And if setImageURI still getting exception then refer this stackoverflow.com/questions/15377186/…

@CBMurphy 2017-12-15 18:38:16

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

@Ezio 2018-04-24 13:13:42

It does not handle all the scenarios. Glide is not a one stop solution, we are using Glide but still facing many OOM crashes

@CBMurphy 2018-05-02 17:32:18

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.

@Sunil Kumar 2017-07-20 15:46:18

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;
    }

@Prakash 2017-07-12 14:38:43

If you are lazy like me. you can start using Picasso library to load images. http://square.github.io/picasso/

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);

@Android Dev 2012-08-13 11:39:42

This issue only happens in Android emulators. I also faced this issue in an emulator but when I checked in a device then it worked fine.

So please check in a device. It may be run in device.

@Sakiboy 2017-06-12 04:38:34

So not true, this happens on MANY Samsung devices...

@Raju yourPepe 2012-08-08 04:36:25

Such OutofMemoryException cannot be totally resolved by calling the System.gc() and so on .

By referring to the Activity Life Cycle

The Activity States are determined by the OS itself subject to the memory usage for each process and the priority of each process.

You may consider the size and the resolution for each of the bitmap pictures used. I recommend to reduce the size ,resample to lower resolution , refer to the design of galleries (one small picture PNG , and one original picture.)

@Ephraim 2011-12-15 22:52:23

I come from iOS experience and I was frustrated to discover an issue with something so basic as loading and showing an image. After all, everyone that is having this issue is trying to display reasonably sized images. Anyway, here are the two changes that fixed my problem (and made my app very responsive).

1) Every time you do BitmapFactory.decodeXYZ(), make sure to pass in a BitmapFactory.Options with inPurgeable set to true (and preferably with inInputShareable also set to true).

2) NEVER use Bitmap.createBitmap(width, height, Config.ARGB_8888). I mean NEVER! I've never had that thing not raise memory error after few passes. No amount of recycle(), System.gc(), whatever helped. It always raised exception. The one other way that actually works is to have a dummy image in your drawables (or another Bitmap that you decoded using step 1 above), rescale that to whatever you want, then manipulate the resulting Bitmap (such as passing it on to a Canvas for more fun). So, what you should use instead is: Bitmap.createScaledBitmap(srcBitmap, width, height, false). If for whatever reason you MUST use the brute force create method, then at least pass Config.ARGB_4444.

This is almost guaranteed to save you hours if not days. All that talk about scaling the image, etc. does not really work (unless you consider getting wrong size or degraded image a solution).

@Jan-Terje Sørensen 2012-02-10 21:01:25

BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true; and Bitmap.createScaledBitmap(srcBitmap, width, height, false); solved my issue I had with out of memory exception on android 4.0.0. Thanks mate!

@rOrlig 2012-03-01 02:26:37

In Bitmap.createScaledBitmap() call you should probably use true as the flag parameter. Otherwise the quality of the image will not be smooth when scaling up. Check this thread stackoverflow.com/questions/2895065/…

@Genia S. 2012-09-03 09:47:31

That really is fabulous advice. Wish I could give you an extra +1 for taking Google to task for this amazingly rinky dink bug. I mean... if it's not a bug then the documentation really needs to have some seriously flashing neon signs saying "THIS IS HOW YOU PROCESS PHOTOS", cause I've been struggling with this for 2 years and just now found this post. Great find.

@Dave 2012-10-19 20:58:56

Scaling your images down definitely helps, but this is an important step and what ultimately solved this issue for me. The problem with just scaling your images is if you have a lot of them, or if the source images are very large then you can still run into the same problem. +1 to you Ephraim.

@sai 2013-12-13 12:56:01

@ToAllTheAboveWhoClaimedTheirProblemSolved : Sorry to ask if wrong, But I just have some ImageViews(1280*720) and needs to scale them all the way from ldpi devices to xhdpi devices . Ok by using above lines "Bitmap.createScaledBitmap(srcBitmap, width, height, false);" . What will be my srcBitmap, width & height parameters?(Width and height of the ANDROID Device(s) I don't know). Please explain this sensitive issue.

@Felixqk 2014-06-11 10:07:06

Regarding to suggestion 1, does it work on Transparent or semi-transparent image? I have some problems when processing on small images send to Android notification bar, it either didn't display or display wrong things

@Riverside 2014-10-17 11:32:38

I know saying thanks in comments in not done on this forum. But after 3 weeks OOM debugging, I finaly found this post, which solved all my headaches. Also for me... want to give some more +1 but can't. Why is this not in the books, looks like a hidden treasure in bitmap handling..

@Denis Kniazhev 2014-12-31 12:53:13

As of Lollipop, BitmapFactory.Options.inPurgeable and BitmapFactory.Options.inInputShareable are deprecated developer.android.com/reference/android/graphics/…

@Marilia 2017-05-01 23:30:18

Google now advises against using inPurgeable. They now recommend using a different flag, inBitmap. More info here: developer.android.com/reference/android/graphics/… You may also find it interesting to read this: code.facebook.com/posts/366199913563917

@Himanshu Mori 2015-05-19 05:51:36

unfortunately if None of the Above works, then Add this to your Manifest file. Inside application tag

 <application
         android:largeHeap="true"

@Stealth Rabbi 2016-03-28 15:49:15

Can you explain what this actually does? Simply telling people to add this doesn't help.

@Prakash 2017-07-12 14:42:47

This is a very bad solution. Basically you are not trying to fix the problem. Instead asking android system to allocate more heap space for your application. This will have very bad implications on your app like your app consuming lot of battery power as GC has to run through large heap space to clean up memory and also your app performance will be slower.

@Himanshu Mori 2017-07-13 13:00:10

then why android is allowing us add this android:largeHeap="true" in our manifest? Now you are challenging Android.

@AdamK 2012-04-12 16:31:31

The Android Training class, "Displaying Bitmaps Efficiently", offers some great information for understanding and dealing with the exception java.lang.OutOfMemoryError: bitmap size exceeds VM budget when loading Bitmaps.


Read Bitmap Dimensions and Type

The BitmapFactory class provides several decoding methods (decodeByteArray(), decodeFile(), decodeResource(), etc.) for creating a Bitmap from various sources. Choose the most appropriate decode method based on your image data source. These methods attempt to allocate memory for the constructed bitmap and therefore can easily result in an OutOfMemory exception. Each type of decode method has additional signatures that let you specify decoding options via the BitmapFactory.Options class. Setting the inJustDecodeBounds property to true while decoding avoids memory allocation, returning null for the bitmap object but setting outWidth, outHeight and outMimeType. This technique allows you to read the dimensions and type of the image data prior to construction (and memory allocation) of the bitmap.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it, unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.


Load a scaled down version into Memory

Now that the image dimensions are known, they can be used to decide if the full image should be loaded into memory or if a subsampled version should be loaded instead. Here are some factors to consider:

  • Estimated memory usage of loading the full image in memory.
  • The amount of memory you are willing to commit to loading this image given any other memory requirements of your application.
  • Dimensions of the target ImageView or UI component that the image is to be loaded into.
  • Screen size and density of the current device.

For example, it’s not worth loading a 1024x768 pixel image into memory if it will eventually be displayed in a 128x96 pixel thumbnail in an ImageView.

To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize to true in your BitmapFactory.Options object. For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384. Loading this into memory uses 0.75MB rather than 12MB for the full image (assuming a bitmap configuration of ARGB_8888). Here’s a method to calculate a sample size value that is a power of two based on a target width and height:

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) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

Note: A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the inSampleSize documentation.

To use this method, first decode with inJustDecodeBounds set to true, pass the options through and then decode again using the new inSampleSize value and inJustDecodeBounds set to false:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

This method makes it easy to load a bitmap of arbitrarily large size into an ImageView that displays a 100x100 pixel thumbnail, as shown in the following example code:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

You can follow a similar process to decode bitmaps from other sources, by substituting the appropriate BitmapFactory.decode* method as needed.

@rene 2015-09-07 08:44:21

This answer is being discussed on meta

@FallenAngel 2015-09-07 10:02:23

This answer (except the information reached through the link) do not offer much of a solution as for an answer. The important parts of the link should be merged into the question.

@Martijn Pieters 2015-09-08 18:44:35

This answer, like the question and the other answers are Community Wiki, so this is something the community can fix by editing, something that does not require moderator intervention.

@Fedor 2009-05-05 09:00:14

To fix the OutOfMemory error, you should do something like this:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

This inSampleSize option reduces memory consumption.

Here's a complete method. First it reads image size without decoding the content itself. Then it finds the best inSampleSize value, it should be a power of 2, and finally the image is decoded.

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}

@Mirko N. 2010-05-28 11:59:26

Note that 10 may not be the best value for inSampleSize though, the documentation suggests using powers of 2.

@Flynn81 2010-07-08 15:19:17

I'm facing the same problem as Chrispix, but I don't think the solution here really solves the problem, but rather sidesteps it. Changing the sample size reduces the amount of memory used (at the cost of image quality, which is probably okay for an image preview), but it will not prevent the exception if a large enough image stream is decoded, of if multiple image streams are decoded. If I find a better solution (and there may not be one) I'll post an answer here.

@stealthcopter 2010-08-08 22:43:15

You only need an appropriate size to match the screen in pixel density, for zooming in and such you can take a sample of the image at a higher density.

@sgarman 2011-04-06 18:27:55

Can anyone explain what the REQUIRED_SIZE corresponds to? Is that pixels of one side, dips? Thanks!

@Fedor 2011-04-07 00:35:43

REQUIRED_SIZE is the new size you want to scale to.

@Fraggle 2011-08-19 01:57:33

See my answer below, I had same problem and got it working simply by calling a different BitmapFactory method.

@Bostone 2012-01-09 19:28:24

The loop looks better with this change: while (tmpW > MAX_IMG_SIZE || tmpH > MAX_IMG_SIZE) { tmpW /= 2; tmpH /= 2; scale *= 2; }

@Bostone 2012-01-09 19:32:39

Also if you are dealing with streams you will have to request image anew after executing the first call or you'll get nothing

@rOrlig 2012-02-27 16:15:19

Another thing that helps is to call recycle() once the Bitmap is not going to be used anymore...

@cesar 2012-03-28 02:55:55

why do you further divide o.outWidth / scale by 2?

@Dave 2012-04-19 15:46:25

Since you're doing powers of 2, instead of scale *=2, you should do scale >> 2. For divide, you can do scale << 2.

@Kishore 2012-06-28 11:28:27

@Fedor i have tried your code it works fine when i process bitmaps in a listview in a single activity, if i use the same in tabs with list of images in each tab, getting out of memory error. How to deal with it?

@Stefan Anca 2012-08-26 21:46:19

Great answer! But how do you get REQUIRED_SIZE dynamically at runtime (catering for different size displays)? This must be done once the View is being drawn but before the Bitmap is expanded.

@Edmund Rojas 2012-10-22 01:46:42

Is 70 the safe size for sampling? or would it make a difference if I used a bigger size?

@user1106888 2013-07-08 20:21:34

this solution helped me but the image quality is terrible. I am using a viewfilpper to display the images any suggestions?

@android developer 2013-07-27 22:44:14

for removing the restrictions of using a power of 2 for sampling, you can use the next post: stackoverflow.com/questions/16408505/… . also for storing large bitmaps on native memory , you can use the next post:stackoverflow.com/questions/17900732/… . if you keep decoding different bitmaps to the same resolution, consider re-using the same bitmap, as shown here: stackoverflow.com/questions/17834024/… .

@Nguyen Minh Binh 2013-10-11 03:20:36

I usually set options.inPreferredConfig = Bitmap.Config.ALPHA_8;. With this setting, each pixcel will be save by 1 byte instead of 4 bytes as default.

@Travis 2014-01-02 15:53:27

@Dopyiii FYI *=2 is equivalent to >> 1, not >> 2

@joao2fast4u 2014-07-01 15:26:37

@Fedor Shouldn't the catch block for FNFException have at least a Log? Empty catch blocks are always dangerous.

@user276648 2015-03-17 02:47:48

Note that if this is still not enough to avoid OutOfMemoryError, you could use android:largeHeap="true" in your manifest.

@Joolah 2015-07-19 08:52:51

Hello, is there a away to specify the output image to be of a size of 400K. ?

@Swami 2015-08-27 19:55:21

Does Piccasso take care of this?

@Hoo 2017-01-06 12:39:10

But image looked blurry :/

@richa shah 2019-09-10 09:08:12

REQUIRED_SIZE is in which format kb mb gb ?

@Exceptional 2013-07-24 12:00:55

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();

@slott 2015-12-16 07:30:01

Calling .gc is useless. It merely hints to the compiler that now is a good time to do GC - but there is no guarantee that it actually happens at this point in time.

@Gaurav Pansheriya 2014-01-24 12:57:55

use these code for every image in select from SdCard or drewable to convert bitmap object.

Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    bitmap = Bitmap.createScaledBitmap(BitmapFactory
        .decodeFile(ImageData_Path.get(img_pos).getPath()),
        width, height, true);
} catch (OutOfMemoryError e) {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.RGB_565;
    options.inSampleSize = 1;
    options.inPurgeable = true;
    bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
        .getPath().toString(), options), width, height,true);
}
return bitmap;

use your image path instend of ImageData_Path.get(img_pos).getPath() .

@Prerna 2011-06-04 09:44:58

I have resolved the same issue in the following manner.

Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
    b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
    b.eraseColor(0xFFFFFFFF);
    Rect r = new Rect(0, 0,320 , 424);
    Canvas c = new Canvas(b);
    Paint p = new Paint();
    p.setColor(0xFFC0C0C0);
    c.drawRect(r, p);
    d = mContext.getResources().getDrawable(mImageIds[position]);
    d.setBounds(r);
    d.draw(c);

    /*   
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inTempStorage = new byte[128*1024];
        b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
        o2.inSampleSize=16;
        o2.inPurgeable = true;
    */
} catch (Exception e) {

}
i.setImageBitmap(b);

@ckpatel 2013-05-25 05:17:37

that's all Right but i'm using multiple bitmap for draw circle in OnCreate and activity call 4-5 times so how to clear bitmap and how to remove bitmap and create again for second time when activity 0nCreate..

@Luke Taylor 2012-06-09 20:33:32

This worked for me!

public Bitmap readAssetsBitmap(String filename) throws IOException {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inPurgeable = true;
        Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
        if(bitmap == null) {
            throw new IOException("File cannot be opened: It's value is null");
        } else {
            return bitmap;
        }
    } catch (IOException e) {
        throw new IOException("File cannot be opened: " + e.getMessage());
    }
}

@Chrispix 2009-02-16 06:23:31

I did the following to take the image and resize it on the fly. Hope this helps

Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);    

@Fedor 2010-01-05 06:51:34

This approach scales the bitmap. But it doesn't solve the OutOfMemory issue because the full bitmap is being decoded anyway.

@Chrispix 2010-09-22 17:19:38

I will see if I can look at my old code, but I think it did solve my out of memory issues. Will double check my old code.

@NoBugs 2013-05-21 06:21:31

In this example at least, it looks like you aren't keeping the reference to the full bitmap, thus the memory savings.

@Pamela Sillah 2018-02-07 07:18:57

For me it did solve the memory issue, but reduced the quality of the images.

@Arsalan 2010-11-07 11:08:19

Use this bitmap.recycle(); This helps without any image quality issue.

@Artem Russakovskii 2011-09-28 01:01:12

According to the API, calling recycle() is not needed.

@Fattie 2014-06-21 10:58:58

Yes, this is wrong. Full solution .. stackoverflow.com/a/24135283/294884

@user1602915 2014-12-18 09:40:38

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

@Anto Binish Kaspar 2011-05-24 20:17:04

It's a known bug, it's not because of large files. Since Android Caches the Drawables, it's going out of memory after using few images. But I've found an alternate way for it, by skipping the android default cache system.

Solution: Move the images to "assets" folder and use the following function to get BitmapDrawable:

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}

@Wroclai 2012-08-24 10:32:15

I have seen a lot of questions about OOM exceptions and caching lately. The developer guide has a really good article on this, but some tends to fail on implementing it in a suitable way.

Because of this I wrote an example application that demonstrates caching in an Android environment. This implementation has not yet gotten an OOM.

Look at the end of this answer for a link to the source code.

Requirements:

  • Android API 2.1 or higher (I simply could not manage to get the available memory for an application in API 1.6 - that is the only piece of code that doesn't work in API 1.6)
  • Android support package

Screenshot

Features:

  • Retains the cache if there is an orientation change, using a singleton
  • Use one eighth of the assigned application memory to the cache (modify if you want)
  • Large bitmaps gets scaled (you can define the maximum pixels that you want to allow)
  • Controls that there is an internet connection available before downloading the bitmaps
  • Makes sure that you are only instantiating one task per row
  • If you are flinging the ListView away, it simply won't download the bitmaps between

This does not include:

  • Disk caching. This should be easy to implement anyway - just point to a different task that grabs the bitmaps from the disk

Sample code:

The images that are being downloaded are images (75x75) from Flickr. However, put whatever image urls you want to be processed, and the application will scale it down if it exceeds the maximum. In this application the urls are simply in a String array.

The LruCache has a good way to deal with bitmaps. However, in this application I put an instance of an LruCache inside another cache class that I created in order to get the application more feasible.

Cache.java's critical stuff (the loadBitmap() method is the most important):

public Cache(int size, int maxWidth, int maxHeight) {
    // Into the constructor you add the maximum pixels
    // that you want to allow in order to not scale images.
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;

    mBitmapCache = new LruCache<String, Bitmap>(size) {
        protected int sizeOf(String key, Bitmap b) {
            // Assuming that one pixel contains four bytes.
            return b.getHeight() * b.getWidth() * 4;
        }
    };

    mCurrentTasks = new ArrayList<String>();    
}

/**
 * Gets a bitmap from cache. 
 * If it is not in cache, this method will:
 * 
 * 1: check if the bitmap url is currently being processed in the
 * BitmapLoaderTask and cancel if it is already in a task (a control to see
 * if it's inside the currentTasks list).
 * 
 * 2: check if an internet connection is available and continue if so.
 * 
 * 3: download the bitmap, scale the bitmap if necessary and put it into
 * the memory cache.
 * 
 * 4: Remove the bitmap url from the currentTasks list.
 * 
 * 5: Notify the ListAdapter.
 * 
 * @param mainActivity - Reference to activity object, in order to
 * call notifyDataSetChanged() on the ListAdapter.
 * @param imageKey - The bitmap url (will be the key).
 * @param imageView - The ImageView that should get an
 * available bitmap or a placeholder image.
 * @param isScrolling - If set to true, we skip executing more tasks since
 * the user probably has flinged away the view.
 */
public void loadBitmap(MainActivity mainActivity, 
        String imageKey, ImageView imageView,
        boolean isScrolling) {
    final Bitmap bitmap = getBitmapFromCache(imageKey); 

    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.ic_launcher);
        if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                mainActivity.internetIsAvailable()) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                    mainActivity.getAdapter());
            task.execute();
        }
    } 
}

You shouldn't need to edit anything in the Cache.java file unless you want to implement disk caching.

MainActivity.java's critical stuff:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (view.getId() == android.R.id.list) {
        // Set scrolling to true only if the user has flinged the       
        // ListView away, hence we skip downloading a series
        // of unnecessary bitmaps that the user probably
        // just want to skip anyways. If we scroll slowly it
        // will still download bitmaps - that means
        // that the application won't wait for the user
        // to lift its finger off the screen in order to
        // download.
        if (scrollState == SCROLL_STATE_FLING) {
            mIsScrolling = true;
        } else {
            mIsScrolling = false;
            mListAdapter.notifyDataSetChanged();
        }
    } 
}

// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {           
    View row = convertView;
    final ViewHolder holder;

    if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.main_listview_row, parent, false);  
        holder = new ViewHolder(row);
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }   

    final Row rowObject = getItem(position);

    // Look at the loadBitmap() method description...
    holder.mTextView.setText(rowObject.mText);      
    mCache.loadBitmap(MainActivity.this,
            rowObject.mBitmapUrl, holder.mImageView,
            mIsScrolling);  

    return row;
}

getView() gets called very often. It's normally not a good idea to download images there if we haven't implemented a check that ensure us that we won't start an infinite amount of threads per row. Cache.java checks whether the rowObject.mBitmapUrl already is in a task and if it is, it won't start another. Therefore, we are most likely not exceeding the work queue restriction from the AsyncTask pool.

Download:

You can download the source code from https://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip.


Last words:

I have tested this for a few weeks now, I haven't gotten a single OOM exception yet. I have tested this on the emulator, on my Nexus One and on my Nexus S. I have tested image urls that contain images that were in HD quality. The only bottleneck is that it takes more time to download.

There is only one possible scenario where I can imagine that the OOM will appear, and that is if we download many, really big images, and before they get scaled and put into cache, will simultaneously take up more memory and cause an OOM. But that isn't even an ideal situation anyway and it most likely won't be possible to solve in a more feasible way.

Report errors in the comments! :-)

@Thomas Vervest 2010-08-23 15:25:32

I've made a small improvement to Fedor's code. It basically does the same, but without the (in my opinion) ugly while loop and it always results in a power of two. Kudos to Fedor for making the original solution, I was stuck until I found his, and then I was able to make this one :)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

@Fedor 2010-08-24 01:32:46

Yes you're right while is not so beautiful. I just tried to make it clear to everyone. Thanks for your code.

@DougW 2010-10-13 07:42:56

@Thomas Vervest - There's a big problem with that code. ^ doesn't raise 2 to a power, it xors 2 with the result. You want Math.pow(2.0, ...). Otherwise, this looks good.

@Thomas Vervest 2010-10-26 09:20:51

Ooh, that's a very good one! My bad, I'll correct it immediately, thanks for the reply!

@nicky 2011-01-20 22:53:54

whats , IMAGE_SIZE in this ?

@Thomas Vervest 2011-01-31 22:36:54

IMAGE_MAX_SIZE is the maximum resulting image size (in pixels)

@matsev 2011-02-15 08:21:41

You are creating two new FileInputStreams, one for each call to the BitmapFactory.decodeStream(). Don't you have to save a reference to each of them so that they can be closed in a finally block?

@Adinia 2011-03-03 08:30:46

@Thomas Vervest What other changes should I do in Fedor's code in order to use your piece of code, beside setting a value for IMAGE_MAX_SIZE? I am specially interested to use fis.close(); but it throws error: Unhandled exception type IOException.

@Thomas Vervest 2011-03-07 11:44:57

@Adinia It works fine here... Have you copy-pasted the above code?

@Adinia 2011-03-08 09:56:31

@Thomas Vervest Yes, just replaced Fedor's whole decodeFile() with the above code. But now I just discovered your post about image caching and maybe I'll manage to find the answer there.

@James 2011-05-05 07:50:59

I have a lot of images in my drawable folder. I want to reduce it's size , but decodeFile accepts the parameter as file.How can I make it the image from drawable image and send it this decodeFile.

@Thomas Vervest 2011-05-08 22:39:43

@James I don't quite get your question.. Are you trying store a downscaled image? Because then you can just save the original image to a file, downscale it while opening the file, write the new image to a new file and remove the old file. I don't think this is the cleanest solution, but it is the first one that pops into my head :p

@Glenn Bech 2011-07-17 08:10:48

I think there is a small typo in the code. "scale" should either be cast to an int or declared as a double.

@Artem Russakovskii 2011-09-30 21:22:53

It actually doesn't compile properly unless I set per Glenn's recommendation, so I'd say it's more than a matter of choice.

@Jana 2011-11-04 09:00:30

I tried doing the same with Bitmap by converting them to byte[] and decode them instead of BitmapFactory.decodeStream(); But the image remains the same. Here is the code that i tried pastebin.com/EbXkXCtx. Can anyone help me with this?

@boostedz06 2012-11-30 15:13:48

I really dont get this because my desired images are 370/370 and that is exactly how they come into my app so the scale is always 1 and I get the exceeded heap crash after so many loads...

@Thomas Vervest 2012-12-01 17:37:02

@boostedz06 have you tried weak referencing them? Even if your images are just 370x370, if you allocate enough of them over time and they are referenced Java will never release them. Read up on weak references here weblogs.java.net/blog/2006/05/04/understanding-weak-referenc‌​es, and convert your solution to use them, it'll probably fix your issue.

@erdomester 2012-12-17 20:19:05

How to use this code? How to call decodeFile()?

@Ilya Gazman 2013-05-10 12:03:16

No need try/catch. BitmapFactory.decodeStream already have it inside. IT will return null if error found

@Thomas Vervest 2013-05-11 13:43:53

@Babibu: although that's true, the FileInputStream close method does require you to handle an IOException ;)

@Ilya Gazman 2013-05-11 14:06:04

@ThomasVervest I am not sure you even need to close it. I think this is also done in BitmapFactory.decodeStream

@Thomas Vervest 2013-05-12 21:32:25

@Babibu The documentation doesn't state that the stream is closed for you, therefore I assume it should still be closed. An interesting, and related, discussion can be found here. Note the comment by Adrian Smith, which relates directly to our debate.

@S.Thiongane 2014-01-27 01:39:22

This is really a very Interesting discussion. @ThomasVervest nice tip with the if instead of while ;)

@Qualtagh 2015-02-10 07:32:46

An optimized equivalent version: no floating-point arithmetic, no pow or log functions. scale = 1 << (Integer.SIZE - Integer.numberOfLeadingZeros((Math.max(o.outHeight, o.outWidth) - 1) / IMAGE_MAX_SIZE));

@Qualtagh 2015-02-10 10:14:24

Slightly better: scale = Math.max(Integer.highestOneBit((Math.max(o.outHeight, o.outWidth) - 1) / IMAGE_MAX_SIZE) << 1, 1);

@Himanshu Agarwal 2015-04-10 15:04:26

@ThomasVervest I am trying your code to decode Base64 String which I am getting from web server of my application, I tried writing that String into file and passed that file into above code but it does't decode to Bitmap. And in some devices it throws OOM exceptions too.

@Thomas Vervest 2015-04-12 12:43:54

@HimanshuAgarwal I don't think BitmapFactory decodes base64 encoded images. Have you tried decoding the base64 date into binary data first? What size is your image? The OOM exception could be caused by the file simply being too large for the device to handle, but admittedly this is a bit of a guess.

@Himanshu Agarwal 2015-04-13 04:38:21

@ThomasVervest I am writing base64 string into file and then i am using above method to get Bitmap from that file. Here's my code pastebin.com/ehwyiBZe

@Joolah 2015-07-19 09:01:45

Could you change IMAGE_MAX_SIZE to have a value in memory size . i.e. I want the IMAGE_MAX_SIZE to be 400K. ?

@MukeshThakur 2013-12-21 11:10:09

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();
                }

@Zala Janaksinh 2014-05-30 07:45:37

I think you miss to put the getResizedBitmap() method code!!!

@Jitesh Upadhyay 2013-12-05 10:22:49

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 ;
}

@Bruce 2013-08-31 03:04:47

All the solutions here require setting a IMAGE_MAX_SIZE. This limits devices with more powerful hardware and if the image size is too low it looks ugly on the HD screen.

I came out with a solution that works with my Samsung Galaxy S3 and several other devices including less powerful ones, with better image quality when a more powerful device is used.

The gist of it is to calculate the maximum memory allocated for the app on a particular device, then set the scale to be lowest possible without exceeding this memory. Here's the code:

public static 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();
        }

        // In Samsung Galaxy S3, typically max memory is 64mb
        // Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
        // If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
        // We try use 25% memory which equals to 16mb maximum for one bitmap
        long maxMemory = Runtime.getRuntime().maxMemory();
        int maxMemoryForImage = (int) (maxMemory / 100 * 25);

        // Refer to
        // http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
        // A full screen GridView filled with images on a device with
        // 800x480 resolution would use around 1.5MB (800*480*4 bytes)
        // When bitmap option's inSampleSize doubled, pixel height and
        // weight both reduce in half
        int scale = 1;
        while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
        scale *= 2;

        // 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;
}

I set the maximum memory used by this bitmap to be 25% of maximum allocated memory, you may need to adjust this to your needs and make sure this bitmap is cleaned up and don't stay in memory when you've finished using it. Typically I use this code to perform image rotation (source and destination bitmap) so my app needs to load 2 bitmaps in memory at the same time, and 25% gives me a good buffer without running out of memory when performing image rotation.

Hope this helps someone out there..

@Danyal Aytekin 2014-05-21 16:40:19

+1 for looking at available memory

@Sunil Kumar 2013-08-29 06:34:00

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

@matsoftware 2013-07-14 09:08:14

My 2 cents: i solved my OOM errors with bitmaps by:

a) scaling my images by a factor of 2

b) using Picasso library in my custom Adapter for a ListView, with a one-call in getView like this: Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);

@Chrispix 2013-07-15 13:15:02

I am glad you mentioned Picasso, because it makes loading images much easier. Especially remotely stored ones.

Related Questions

Sponsored Content

37 Answered Questions

38 Answered Questions

[SOLVED] Lazy load of images in ListView

2 Answered Questions

0 Answered Questions

grid view and adapter not working in android

  • 2019-02-04 13:40:33
  • safaa
  • 132 View
  • 0 Score
  • 0 Answer
  • Tags:   android gridview

0 Answered Questions

Issue with base adapter in Kotlin

1 Answered Questions

[SOLVED] Binary XML file line #28: Binary XML file line #28: Error inflating class <unknown>

  • 2018-05-18 05:43:55
  • Александр Волошиновский
  • 551 View
  • 1 Score
  • 1 Answer
  • Tags:   android

1 Answered Questions

Android DatePicker crashes when setting minDate or maxDate on kitkat

  • 2017-12-24 02:11:12
  • user3718908
  • 448 View
  • 2 Score
  • 1 Answer
  • Tags:   android

2 Answered Questions

1 Answered Questions

[SOLVED] Android ADB Logcat-- showing full logcat

2 Answered Questions

Sponsored Content