1

I have a use case where the user takes a picture and the picture has to be saved on sd card .

I have set the orientation of the camera as portrait .

While saving the photo on to SD card i am getting out of memory .

Here is the log -

java.lang.RuntimeException: An error occured while executing doInBackground()
  at android.os.AsyncTask$3.done(AsyncTask.java:300)
  at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
  at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
  at java.util.concurrent.FutureTask.run(FutureTask.java:242)
  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
  at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.OutOfMemoryError
  at android.graphics.Bitmap.nativeCreate(Native Method)
  at android.graphics.Bitmap.createBitmap(Bitmap.java:809)
  at android.graphics.Bitmap.createBitmap(Bitmap.java:786)
  at android.graphics.Bitmap.createBitmap(Bitmap.java:718)
  at com.philips.cl.di.haircare.util.AppUtility.changeOrientationAndwriteDataToFile(AppUtility.java:382)
  at com.philips.cl.di.haircare.mirror.MirrorCameraViewFragment$SaveBitMap.doInBackground(MirrorCameraViewFragment.java:389)
  at com.philips.cl.di.haircare.mirror.MirrorCameraViewFragment$SaveBitMap.doInBackground(MirrorCameraViewFragment.java:369)
  at android.os.AsyncTask$2.call(AsyncTask.java:288)
  at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  ... 4 more

Here is the function that i am using to save -

public static boolean changeOrientationAndwriteDataToFile(Context context,
        final File pictureFile, final byte[] data, final int camId)
        throws Exception {

    FileOutputStream fos = null;
    Bitmap realImage = null;
    try {

        fos = new FileOutputStream(pictureFile);
        realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(camId, info);
        Matrix matrix = new Matrix();

        int rotation = info.orientation;
        int camfacing = info.facing;

        if (camfacing == 1) {
            matrix.setScale(1, -1);
        }
        if (rotation != 0) {
            matrix.postRotate(rotation);

        } else {
            matrix.postRotate(-90);
        }
        realImage = Bitmap.createBitmap(realImage, 0, 0,
                realImage.getWidth(), realImage.getHeight(), matrix, false);
        MediaScannerConnection.scanFile(context,
                new String[] { pictureFile.getPath() },
                new String[] { "image/jpeg" }, null);

        // 100 means maximum quality
        return realImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
    } catch (final Exception e) {
        throw e;

    } finally {
        if (realImage != null) {
            realImage.recycle();
        }
        if (fos != null) {
            fos.close();
        }
    }

}

Line number - 382 is going out of memory .

realImage = Bitmap.createBitmap(realImage, 0, 0,
                realImage.getWidth(), realImage.getHeight(), matrix, false);

Please help on how to resolve this .

Thanks.

Anukool
  • 5,301
  • 8
  • 29
  • 41
  • `data (byte[])` should be garbage-collected before your second version of the image is created so it should not be the cause for the `OutOfMemory` error. You could initialize `fos` later so that it gets allocated after the first version of realImage was eligible for garbage-collection (I think Java sometimes optimizes these things by itself but in this case it is too complex). – Johannes Aug 13 '15 at 08:50
  • Maybe you could work around the problem by using method 2 (canvas instead of new bitmap) in the first answer of http://stackoverflow.com/questions/8552298/how-to-mirror-an-image-file-2-2 – Johannes Aug 13 '15 at 11:22
  • I just analyzed memory using Android studio and as soon as the bitmap is created , the heap increases its size by around 18 mb . This is strange , for a much smaller byte array why does the heap grow by 18 mb . – Anukool Aug 13 '15 at 12:29
  • 18 MiB looks like you have an uncompressed 6 megapixel picture. – Johannes Aug 18 '15 at 15:49

3 Answers3

3

I solved it using BitmapFactory Options Here is the updated method -

public static boolean changeOrientationAndwriteDataToFile(Context context,
        final File pictureFile, final byte[] data, final int camId, int reqWidth , int reqHeight )
        throws Exception {

    FileOutputStream fos = null;
    Bitmap realImage = null;
    Bitmap transformedImage = null ;
    try {

        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(camId, info);
        int rotation = info.orientation;
        int camfacing = info.facing;

        // Out of memory issue fixx -with the sample size concept

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

        options.inJustDecodeBounds = true ;

        BitmapFactory.decodeByteArray(data, 0, data.length, options);

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

        System.out.println("Sample Size : " + options.inSampleSize);

        options.inPreferredConfig = Bitmap.Config.RGB_565;

        options.inJustDecodeBounds = false;

        realImage= BitmapFactory.decodeByteArray(data, 0, data.length, options);

        Matrix matrix = new Matrix();

        if (camfacing == 1) {
            matrix.setScale(1, -1);
        }
        if (rotation != 0) {
            matrix.postRotate(rotation);

        } else {
            matrix.postRotate(-90);
        }
        transformedImage = Bitmap.createBitmap(realImage, 0, 0,
                realImage.getWidth(), realImage.getHeight(), matrix, false);
        MediaScannerConnection.scanFile(context,
                new String[] { pictureFile.getPath() },
                new String[] { "image/jpeg" }, null);

        fos = new FileOutputStream(pictureFile);
        // 100 means maximum quality
        return transformedImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
    } catch (final Exception e) {
        throw e;

    } finally {
        if (realImage != null) {
            realImage.recycle();
        }
        if(transformedImage !=null)
        {
            transformedImage.recycle();
        }
        if (fos != null) {
            fos.close();
        }
    }

}

Here - reqWidth and reqHeight is the screen width and screen height as i have to display the bitmap on the full screen .

Thanks.

Anukool
  • 5,301
  • 8
  • 29
  • 41
0

you can add android:largeHeap="true" to your AndroidManifest.xml

This is a workaround. Use with Care.

kweku360
  • 1,025
  • 1
  • 10
  • 7
0

make new bitmap object instead of recreating the realImage

Shivani Gupta
  • 271
  • 3
  • 12