Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android App crashing on Samsung Galaxy S3 (out of memory error)

Tags:

android

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 :-(.

like image 714
user881148 Avatar asked Aug 10 '12 20:08

user881148


2 Answers

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.

like image 159
Dan Lee Avatar answered Oct 26 '22 10:10

Dan Lee


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

like image 41
user2942738 Avatar answered Oct 26 '22 11:10

user2942738