0

I'm getting out of memory issues when I have quite a lot of results come back (good up to about 30). The actual error is:

08-20 10:55:19.820: E/dalvikvm-heap(13483): Out of memory on a 499408-byte allocation.
08-20 10:55:19.835: E/AndroidRuntime(13483): FATAL EXCEPTION: main
08-20 10:55:19.835: E/AndroidRuntime(13483): java.lang.OutOfMemoryError
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:650)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:389)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at com.directenquiries.assessment.tool.Globals.Functions.decodeSampledBitmapFromFile(Functions.java:94)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at com.directenquiries.assessment.tool.db.assets.AssetRow.image(AssetRow.java:119)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at com.directenquiries.assessment.tool.ViewAssetsList.loadTable(ViewAssetsList.java:75)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at com.directenquiries.assessment.tool.ViewAssetsList.onResume(ViewAssetsList.java:60)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1188)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.app.Activity.performResume(Activity.java:5280)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2606)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2644)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1269)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.os.Handler.dispatchMessage(Handler.java:99)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.os.Looper.loop(Looper.java:137)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at android.app.ActivityThread.main(ActivityThread.java:4898)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at java.lang.reflect.Method.invokeNative(Native Method)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at java.lang.reflect.Method.invoke(Method.java:511)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
08-20 10:55:19.835: E/AndroidRuntime(13483):    at dalvik.system.NativeStart.main(Native Method)

The way the images are shrunk is:

public static Bitmap decodeSampledBitmapFromFile(String imagePath, int reqWidth, int reqHeight) {

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

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

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(imagePath, options);
    }

The image comes out of the DB and is set like:

   @Override
        public String imagePath() { 
                if (firstissuePhoto == null) {
                    firstissuePhoto = StationPhotoDB.getFirstPhotoWithStationObjectID(this.stationObjectID);    
                        if(!firstissuePhoto.endsWith("jpg")){
                            firstissuePhoto = "notset";
                        }
                }
                return firstissuePhoto; 

        }



    @Override
    public Bitmap image() {
        if (firstissuePhotoThumb == null) {
            firstissuePhotoThumb =  Functions.decodeSampledBitmapFromFile(imagePath(),200, 200);
        }   
        return firstissuePhotoThumb;
    }

Then when I actually use the image, and set it, it's like:

String picturePath = dbRow.imagePath();
if (picturePath.equals("notset")) {
            holder.ivPicture.setImageResource(R.drawable.photonotset);      
         } else {
            holder.ivPicture.setImageBitmap(dbRow.image());             
          }

Edit: heres the requested routine:

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) {
            if (width > height) {
                inSampleSize = Math.round((float)height / (float)reqHeight);
            } else {
                inSampleSize = Math.round((float)width / (float)reqWidth);
            }
        }
        return inSampleSize;
    }
MissCoder87
  • 2,669
  • 10
  • 47
  • 82

2 Answers2

0

You need to recycle the bitmaps which are not in use. there is a recycle() api. use it

Sushil
  • 8,250
  • 3
  • 39
  • 71
  • Would you mind elaborating a little bit more? At what stage would I recycle? – MissCoder87 Aug 20 '13 at 10:21
  • `recycle` is called on a Bitmap object when you don't need it any more. Thus, you will need to associate the Bitmap with the holder, then do `if (holder.image != null) holder.image.recycle();` before setting the image. This will only ensure that the image is garbage collected and will not really help with the OOM, unless you are creating a lot of bitmaps. Adding `Log` tracing should help with this. See http://stackoverflow.com/questions/3823799/android-bitmap-recycle-how-does-it-work. – reece Aug 20 '13 at 10:52
  • you may also refer to this link, hope it helps http://stackoverflow.com/questions/4959485/bitmap-bitmap-recycle-weakreferences-and-garbage-collection – Darko Rodic Aug 20 '13 at 12:40
0

The code looks correct, and follows the Android best practice for handling large bitmap files.

Looking at the stack trace, the Out of Memory occurs in BitmapFactory.nativeDecodeStream. With JPEG images, the application has to decode (uncompress) the image data. That decompression likely requires the image to be uncompressed into memory before it is scaled. If the image is large (e.g. a photo), this can easily require a large amount of memory.

You may want to try forcing the inSampleSize to be a multiple of 8 as JPEG images can have a block size of 8 or 16. This may help the image scaling logic not using too much memory decoding the JPEG.

Also, logging the width, height and inSampleSize values should be useful.

reece
  • 7,945
  • 1
  • 26
  • 28