1

I'm developing an Android app which uses multiple large images in several of its activities. Each image is around 1280x800 and I load about 2-4 of these images for each Activity. I realize that these images are very large in terms of the memory that is allocated to each individual app on a device, but how can I display them at their original resolution without running into a java.lang.OutOfMemory error? I need these images to be displayed at their full size on a screen (scaling is done automatically by xml when the screen is smaller than the image). I saw several solutions involving shrinking down images into thumbnails and storing those into memory, but wouldn't that cause the image to lose it's original size/resolution? Thanks for your help!

NewGradDev
  • 984
  • 5
  • 15
  • 35

3 Answers3

3

There are a few things you can do about this.

The first thing that comes to mind is that 1280x800 is (probably) your entire screen, so you should only need to display one at a time. Don't hold the others in memory while you do that.

Still a 1280x800 image at 4 bytes per pixel is only 4MB, and tablets seem to all offer 48MB of heap these days. You should be able to hold a few in memory if you need to. If you're running out of memory, it's possible you're leaking. If you watch in DDMS, does your memory usage continue to grow as you change activities?

A common source of leaks is the bitmaps themselves. Be sure to call Bitmap#recycle when you're done with them.

If it really comes down to it, and you cannot fit into the heap space provided, you can also try adding android:largeHeap="true" to the application tag in your manifest. This will request the system offer you more heap space - up to 256MB on some devices. This should be a last resort, though, as it will vary by device, and be completely ignored on some (the original Kindle Fire comes to mind).

You can see just how much total heap space you have with Runtime.getRuntime().maxMemory();. See this answer for a more detailed explanation. It's trickier to see just how much you're using, but there is a description here if you want to tackle that beast.

Finally, there may be a better way to load your images than specifying them in xml. Be sure to read this developer guide page. Even if you must leave them in xml, I have seen marked improvement in memory usage by splitting the image assets into the drawable-hdpi, drawable-mdpi, etc. directories rather than just dumping them in drawable.

Community
  • 1
  • 1
dokkaebi
  • 8,294
  • 3
  • 38
  • 58
  • Thanks for your reply, I actually overcame the problem by resaving my images using Photoshop (for some reason, the original images were messing up the bitmap creation/management process in Android). Also, I used the bitmap.recycle() line to free up memory whenever I change the activity. Unfortunately, whenever I go back to that activity now (using the back button), I get a "Trying to use recycled bitmap" error. I know that I need to reset the bitmap in the onResume() method, but how do I do this? Thanks again! – NewGradDev Jan 09 '13 at 18:50
  • Match up your creation / recycling. If you create the bitmaps in `onStart`, recycle in `onStop`. If you create them in `onResume`, recycle in `onPause`. If they're loaded via xml, don't bother recycling them; it's handled by the framework in that case (but I believe they will be held in memory all the way until `onDestroy`, which may be as long as your application is alive). – dokkaebi Jan 09 '13 at 19:35
  • I understand the theoretical part of the problem, but what code would I use to actually accomplish the solution? I guess I don't know how to get the corresponding bitmaps for each ImageView and link them all together after I call the recycle(). Could you help me out with that code? Thanks for your help... this is last obstacle I need to overcome before I launch my app and it would be awesome if you could help me with this code. :) – NewGradDev Jan 10 '13 at 01:29
  • Based on this part of your question: "(scaling is done automatically by xml when the screen is smaller than the image)", I assume you are just referencing your bitmaps in xml like `android:src="@drawable/..."`. If so, you do not need to recycle them. If the framework loads them, the framework frees them. If you are loading them in code, post that code and I'll have a look. Your first comment suggests that you are not having memory problems after all, but if you are, you should start leak hunting as per Ramesh's answer. – dokkaebi Jan 10 '13 at 02:28
  • Yeap, I'm loading the images with the android:src="@drawable/..." notation. So what you're saying is that when the activity is put paused (or even destroyed), the bitmaps are automatically released? I only need to do the recycling manually when I assign bitmaps/images in Java code, correct? Also, I'm getting the OOM error at the "setContentView(R.layout.xxx)" when I call a new activity. Basically, I'm in Activity A which has multiple images (all 200x200 and 1 1280x800) and when Activity B is called, it fails at setting the layout content. Thanks! – NewGradDev Jan 10 '13 at 05:14
  • Here's what I've come up with. The framework will recycle its bitmaps when the views are destroyed, but this might not happen for a long time. If A can launch B and B can launch A, and you don't use a flag such as FLAG_ACITIVITY_CLEAR_TOP, the user can create an arbitrary number of activities on the stack. These activities are never destroyed, so those bitmaps aren't recycled. The solution is to recycle the bitmaps yourself in onPause, and load them again dynamically in onResume. Here's how to get the bitmap from an ImageView and recycle it: http://stackoverflow.com/a/7009362/931277 – dokkaebi Jan 12 '13 at 01:05
1

This article describes pretty well how to create a heap dump and analyze it using Eclipse MAT. This will help you find the most likely suspects for memory leaks pretty quickly.

Again I point you to this great link I found from another SO Question that has tutorials of how to properly over come the problem.

n00begon
  • 3,467
  • 3
  • 26
  • 41
Ramesh Sangili
  • 1,575
  • 3
  • 15
  • 30
0

Image needs to scaled before loading the image using BitmapFactory or related methods.

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;
}

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

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

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

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

whole thing is explained in Android developer site, Loading Large Bitmaps Efficiently

Sreejith B Naick
  • 1,195
  • 6
  • 12