1

In my Android app I have a custom ImageView which, based on user interaction, needs to update its image. The ImageView can only have two images, which represent the back and front of a card. So, when the user clicks a button I need to show the back of the card (set my custom's ImageView image to the back of the card and when the user cliks the button again, set the front image as ImageView drawable).

In order to accomplish this I have two methods in my custom ImageView:

public void showBack() {
    setImageResource(R.drawable.card_back);
}

public void showFront() {
    setImageResource(R.drawable.card_front);
}

Of course, this works great, but the memory consumption of my app sometimes get near 114 MB, and it increases as the user clicks the button (rotates the card).

Due to that, I think the problem could be produced by the change of image. The images are small, one is 104.6KB and the other 80.4KB (PNG format), so I don't understand why my app is consuming so much memory (maybe because images get allocated very times and never recycled?, or maybe because of Android's cache system of drawables?)

So, what can I do to solve my problem?

Thank you.

Image dimensions are 335 x 501

Ernestina Juan
  • 957
  • 1
  • 9
  • 24
  • I am having the same issue when I display large image as button background. What I did to reduce the memory usage was to scale down the images that I use and recreate the simple ones with shape in xml. People online suggest to do System.gc before using any bitmap, but my mate thinks we should just let Java handles GC because that is the whole point of it... – Cao Felix Jan 21 '16 at 22:07
  • 1
    Possible reason: If you're storing the images in the drawable folder, Android will consider them to be mdpi and scale them up. Put them in the xhdpi folder instead. – black Mar 03 '16 at 06:00
  • 1
    I was noticing the same phenomenon with relatively small jpeg drawables loading into a ViewPager. I had located the jpegs in the the Drawable folder and they were creating 90MB memory allocations. I moved the files into the xxxhdpi folder and the allocation was about 6MB. It still seems like a lot but made a huge difference as @black mentioned. – GregM Mar 11 '17 at 01:59

3 Answers3

1

So there are a few factors that come into play here. The imageView dimensions (I assume) have been defined in dp . The number of pixels this translates to changes from phone to phone depending on the screen resolution. Hence the same drawable, when attached to imageView in different phones will have to be scaled to match the px required to display it in the imageView.

This scaling requires large amount of memory which causes the OOM exception. Some phones have a large RAM and can complete this scaling without causing any problems, whereas older models may not be able to.

When you load in the imageView, the original image is kept in memory and then rescaled. I suggest getting the pixel dimensions of the imageView and then scaling the image accordingly, to produce the best fit. This will reduce the memory consumption.

This answer and this should help you in getting the dimensions of the imageView.

Bitmap icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_resource);

     private Bitmap ScaleImage(Bitmap bitmap){
        //the original dimension of the bitmap
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int requiredWidth=0;
        int requiredHeight=0;
        //todo: Calculate the required dimensions of the bitmap as per the imageView

        ExifInterface exif;
        Matrix matrix = new Matrix();
        /* In case you need to rotate the bitmap
        try {
            exif = new ExifInterface(value);
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
            Log.d("EXIF", "Exif: " + orientation);
            if (orientation == 6) {
                matrix.postRotate(90);
                Log.d("EXIF", "Exif: " + orientation);
            } else if (orientation == 3) {
                matrix.postRotate(180);
                Log.d("EXIF", "Exif: " + orientation);
            } else if (orientation == 8) {
                matrix.postRotate(270);
                Log.d("EXIF", "Exif: " + orientation);}

        }catch (IOException e){e.printStackTrace();}
       */

        matrix.postScale(requiredWidth/originalWidth, requiredHeight/originalHeight);
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,width, height, matrix, true);
        return resizedBitmap;
    }

And finally

public void showFront() {
    Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
    R.drawable.icon_resource);
    Bitmap scaledBitmap = ScaleImage(icon);
    setImageResource(scaledBitmap);
}

Now ideally you would want to shift the scaling of the image to an Async Task so the main thread is not blocked. Let me know if this works please.

Community
  • 1
  • 1
Varun Agarwal
  • 1,587
  • 14
  • 29
0

The size of the images don't play much of a role here. The dimensions on the other hand do. So if you are using images with large dimensions and using them in ImageView with smaller resolution, then it will be a problem and you might end up having OOM (Out Of Memory Exception).

Please see: http://developer.android.com/training/displaying-bitmaps/index.html

Keshav
  • 577
  • 3
  • 10
0

From my personal experience when loading images you should always use picasso library. It helps with all the memory problems, lets you scale easily etc. http://square.github.io/picasso/

Karolis Jc
  • 106
  • 3