15

I have an Android app that takes some pictures, re-sizes them and sends them over to the back-end server. This app works perfectly fine on all other phones (Gingerbread and Ice cream sandwich) except Samsung Galaxy S3. Whenever it takes the pictures and tries to re-size it, it runs out of memory. Initially I thought it was an issue with the re-sizing part and I implemented the bitmap object with insamplesize and tried everything but it still kept on crashing. Then I realized that when the app launches at first, it is using up a lot of memory. On HTC and all other phones, when my app launches, it uses up about 4 MB but on the Galaxy S3, it uses up about 30 MG - almost 10 times more. I can't seem to figure out what would cause it to use up that much more memory. I am recycling all the objects and layouts properly (I think). Here is the code I have to resize the image:

public static void resizeImage(String pathOfInputImage,
        String pathOfOutputImage) {
    try {
        int inWidth = 0;
        int inHeight = 0;
        float factor;

        InputStream in = new FileInputStream(pathOfInputImage);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, options);
        inWidth = options.outWidth;
        inHeight = options.outHeight;

        in.close();
        in = null;

        options = null;         

        if (inWidth > inHeight) {
            factor = (float) inHeight / 480;
        } else {
            factor = (float) inWidth / 480;
        }           
        options = new BitmapFactory.Options();

        Bitmap roughBitmap;

        if(Build.VERSION.SDK_INT < 12) {
            System.gc();
        }

        try
        {
            in = new FileInputStream(pathOfInputImage);
            options.inSampleSize = 4;
            roughBitmap = BitmapFactory.decodeStream(in, null, options);
        }
        catch (OutOfMemoryError e)
        {
                roughBitmap = Utils.fetchRoughBitmap(pathOfInputImage, factor);
            }
        finally
        {
            roughBitmap = Utils.fetchRoughBitmap(pathOfInputImage, factor);
            }

        in.close();
        in = null;

        boolean rotate = false;
        Bitmap rotatedBitmap = null;

        if (isNeedToRotate(pathOfInputImage)) {
            rotate = true;
            Matrix m = new Matrix();

            m.postRotate(90);
            try
            {
                rotatedBitmap = Bitmap.createBitmap(roughBitmap, 0, 0,
                        roughBitmap.getWidth(), roughBitmap.getHeight(), m,
                        true);
            }
            catch (OutOfMemoryError e)
            {
                rotatedBitmap = Bitmap.createBitmap(roughBitmap, 0, 0,
                        roughBitmap.getWidth()/2, roughBitmap.getHeight()/2, m,
                        true);
            }
        }


        if (rotate) {
            Utils.log(TAG, "Rotate Invoked :------->");
            int temp = inHeight;

            inHeight = inWidth;
            inWidth = temp;
        }

        options.inSampleSize = Math.round(factor);

        float dstWidth = (int) inWidth / factor;
        float dstHeight = (int) inHeight / factor;

        Bitmap resizedBitmap;
        if (rotate) {
            try
            {
                resizedBitmap = Bitmap.createScaledBitmap(rotatedBitmap,
                        (int) dstWidth, (int) dstHeight, true);
            }
            catch (OutOfMemoryError e)
            {
                resizedBitmap = Bitmap.createScaledBitmap(rotatedBitmap,
                        (int) dstWidth/2, (int) dstHeight/2, true);
            }

        } else {

            try
            {
                resizedBitmap = Bitmap.createScaledBitmap(roughBitmap,
                    (int) dstWidth, (int) dstHeight, true);
            }
            catch (OutOfMemoryError e)
            {
                resizedBitmap = Bitmap.createScaledBitmap(roughBitmap,
                        (int) dstWidth/2, (int) dstHeight/2, true);
            }
        }

        try 
        {
            FileOutputStream out = new FileOutputStream(pathOfOutputImage);
            resizedBitmap.compress(Bitmap.CompressFormat.PNG, 80, out);
        }
        catch (Exception e) {
            Utils.log("Image", e.getMessage() + e);
        }

        //Free up the memory.
        roughBitmap.recycle();
        resizedBitmap.recycle();

        if (rotatedBitmap != null) {
            rotatedBitmap.recycle();
        }

    } catch (IOException e) {
        Utils.log("Image", e.getMessage() + e);
    }
}


private static Bitmap fetchRoughBitmap(String pathOfInputImage, float factor)
{
    Bitmap roughBitmap = null;
    try
    {
        FileInputStream in = new FileInputStream(pathOfInputImage);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = Math.round(factor);
        roughBitmap = BitmapFactory.decodeStream(in, null, options);
        return roughBitmap;
    }
    catch (OutOfMemoryError mem_error)
    {
        return Utils.fetchRoughBitmap(pathOfInputImage, factor+1);
    }
    catch (FileNotFoundException e)
    {
        //return Utils.fetchRoughBitmap(pathOfInputImage, factor+1);
        return roughBitmap;
    }
}

This code works fine because I run it through a recursive loop until it doesn't run out of memory but then my resulting image is only about 300x300 px and I need it to be at least 640x480. My questions are:

  1. Can anyone tell me why Galaxy S3 uses up upto 90% memory when it first loads my app instead of 60% on HTC phones.
  2. Is there anything wrong being done in the re-size code that is making my app run out of memory.

Any help will be greatly appreciated as I have already spent 3 days on this issue :-(.

user881148
  • 1,209
  • 2
  • 9
  • 15
  • Can you add some profiling code in an attempt to figure out where the program is ballooning at the waist? – Wug Aug 10 '12 at 20:48
  • Thanks for a prompt response. I am actually not sure how to add profiling code. I just got into Android development and this code was handed over to me. – user881148 Aug 10 '12 at 20:59
  • To be honest, I'm not 100% sure how to do it on android either. I know there is a method in `System` that returns the number of allocated bytes a program has but I'm not sure if Android supports it. It's also not very specific, it will really only do good as a ballpark measurement of parts of your code that trigger a lot of allocations. I think that's all you'll need in this case though. – Wug Aug 10 '12 at 21:51
  • When the app loads up, it loads a splash image and a background image. When I remove these images from while the app is loading, the app works fine. But when I leave them there, it runs out of memory. I was checking the resource directory and we only have images in the Drawable folder, not in any of the drawable-hdpi or ldpi or mdpi folders. So is the device trying to convert these images to fit the phone's resolution and it's using all the memory? – user881148 Aug 13 '12 at 14:32

4 Answers4

9

umm... I know it's an old question now, but here's what I think.

Galaxy S3 uses xhdpi density, therefore allocates bigger heap size on launching, (as far as I know, 32mb)

but if you don't have any xhdpi resources in your res directory, then it will call any default images in drawable or from hdpi which is the closest to xhdpi density.

Then it will expand the image to fit it's neeeds, and at this phases expanding the image will take up much memory, (might) resulting in out of memory issues.

You can either solve this problem by enabling Screen Compatibility Mode or you can just create (if not there by default) drawable-xhdpi, providing Appropriate Resources for xhdpi devices such as Galaxy S3.

Dan Lee
  • 126
  • 1
  • 4
  • I had OOM issue with my app, but mine was solved by changing the build target version to 4.0 instead of 2.2. ( I had to abandon support for device under 4.0 though ) – Dan Lee Dec 11 '12 at 00:00
8

Wow. We have struggled with this Out of Memory issue on the S3 for weeks now. Finally, applied one of the techniques in this thread.

In our app, we had drawable-hdpi have all the images for the app. On most phones, we had no issue . On the S3, the app would take 2x as much memory and then run into out of memory issue.

I just created drawable-xhdpi folder with the same contents as drawable-hdpi folder and ran it on S3. Immediately noticed the memory footprint was 1/2 and no out of memory issues.

This was a frustrating experience. Hopefully other people know to look for this thread and save hours of degugging.

thanks

user2942738
  • 81
  • 1
  • 1
5

I sort of solved the issue myself. The app running out of memory didn't have to do anything with the above code. I added the MAT and I saw that the bitmap objects were being kept alive from previous views. So I removed the graphics from the view and it solved the memory issue.

user881148
  • 1,209
  • 2
  • 9
  • 15
  • Hi, i am also facing the same issue. You have said that you got the solution by "removing the graphics from the view" - what does it means, can you explain this with a code or any link.... – Siva K Oct 25 '12 at 06:16
  • 1
    I overwrote the onDestroy method and added the following line: layout.setBackgroundDrawable(null);. Please let me know if this worked or didn't work for you. – user881148 Oct 25 '12 at 14:34
  • i have already added this line in my code, in on create method. For the first time after capturing the photo, it gets displayed in image view, after going back to camera and capturing a new photo it gets crashed, as like for you... – Siva K Oct 25 '12 at 17:24
  • Here in your code layout refers to image view or any other layout... Exactly at which point you have added those lines, in OnCreate, or Resume anything other...pls help me... – Siva K Oct 25 '12 at 17:26
  • have you tried picking the images from sdcard gallery and placing them in image view of S3, here too it works once and aging gets crashed... all these issues only in S3.... – Siva K Oct 25 '12 at 17:27
  • In my case, the problem wasn't on the page where I took pictures. What happened was that for some reason, my splash screen and my home screen background image bitmaps were getting preserved in the memory from the beginning. So it had very little space left in the memory for the pictures when I took them and tried to resize using above code. Hence I removed the bitmaps from my home screen and my splash screen using the onDestroy method and using the layout.setBackgroundDrawable(null) method. I still don't know why the bitmap objects didn't clear automatically but this worked so I never bothered – user881148 Oct 25 '12 at 22:04
  • 1
    And this only happened in Samsung Galaxy S3 because the phone's resolution is much bigger than any other phones. I checked HTC One and all other phones and the bitmap images were being preserved in all phones but this wasn't an issue because these phones didn't have such large resolution images hence it never ran out of memory even though they were always in the memory. So when you look it up in MAT, see what objects are being kept alive and from what view. Then go in that view and add the onDestroy method and make the background drawable null. – user881148 Oct 25 '12 at 22:05
  • Same issue here, even on Htc One x -uses same density as Galaxy S3- runs perfectly. So, what is the problem with Galaxy S3? – Mustafa Güven Dec 05 '12 at 10:01
0
I overwrote the onDestroy method and added the following line: layout.setBackgroundDrawable(null);. Please let me know if this worked or didn't work for you. 

– user881148

Cool, it worked for me... with a little added for background pics on a layout. Instead of using BitmapDrawables or Bitmap I use Drawables after convert from bitmap.

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.outerspace);
Drawable bitDraw = new BitmapDrawable(getResources(), bitmap);

main.setBackgroundDrawable(bitDraw);

My issue was that is was working on all devices expect for the S3 (which was annoying). But the app is up and running now! Thanx...user881148

abotrix
  • 138
  • 1
  • 12