3

The app I'm creating requires a number of images to be pulled from our server, and displayed on a page. The user can go into several different categories, and each will have their own images. The problem is after going to 2-3 categories (depending on how many images are in those categories) in a row, the app has no more memory and cannot display the Bitmaps without crashing.

What I'd like to be able to do is clear the memory every time the user goes to a new category so that the old category's images won't be stored in memory anymore, freeing up space for the relevant category's images. I'm not sure if this is a good way to do it, or even how to do it if it was.

If anyone has a better solution let me know. One idea that was thrown around was loading only ~20 images at once, and waiting until the user scrolls to the bottom before loading more, however since our customers are paying to have their images on the app, that would cause less traffic to certain images, so this is not the ideal solution. However it's not out of the question.

Here is the code I'm using to load the images: EDIT: My Mistake I posted the wrong code, this is the real code I'm using:

@SuppressWarnings("deprecation")
public Drawable loadImageFromWebOperations(String url, String imagePath) {
    try {
        if(Global.couponBitmaps.get(imagePath) != null){
            scaledHeight = Global.couponBitmaps.get(imagePath).getHeight();
            return new BitmapDrawable(getResources(), Global.couponBitmaps.get(imagePath));
        }
        Drawable d = null;
        File f = new File(getBaseContext().getFilesDir().getPath().toString() + "/" + imagePath + ".png");
        if (f.exists()) {
            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            int scaledWidth = 0;
            try {
                display.getSize(size);
                scaledWidth = size.x;
            } catch (java.lang.NoSuchMethodError ignore) {
                scaledWidth = display.getWidth();
            }
            Bitmap bitmap = null;

            BitmapScaler scaler = new BitmapScaler(f, scaledWidth);
            bitmap = scaler.getScaled();

            scaledHeight = bitmap.getHeight();

            d = new BitmapDrawable(getResources(), bitmap);
            Global.couponBitmaps.put(imagePath, bitmap);
        } else {
            Display display = getWindowManager().getDefaultDisplay();
            Point size = new Point();
            int scaledWidth = 0;
            try {
                display.getSize(size);
                scaledWidth = size.x;
            } catch (java.lang.NoSuchMethodError ignore) {
                scaledWidth = display.getWidth();
            }
            Bitmap bitmap = BitmapFactory.decodeStream((InputStream) new URL(url).getContent());
            int height = bitmap.getHeight();
            int width = bitmap.getWidth();
            scaledHeight =  (int) (((scaledWidth * 1.0) / width) * height);
            f.getParentFile().mkdirs();
            f.createNewFile();
            OutputStream output = new FileOutputStream(f);
            bitmap.compress(Bitmap.CompressFormat.PNG, 90, output);
            output.close();
            bitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false);
            d = new BitmapDrawable(getResources(), bitmap);
            Global.couponBitmaps.put(imagePath, bitmap);
        }
        return d;
    } catch (MalformedURLException e) {
        e.printStackTrace();
        return null;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    } catch (OutOfMemoryError e){
        e.printStackTrace();
        return null;
    }
}

If anyone knows if there is a more efficient way of loading the images, or if there is a way to clear the memory before drawing them, it would be greatly appreciated, thank you.

shadowarcher
  • 3,265
  • 5
  • 21
  • 33
  • 1
    There's some information here: https://developer.android.com/training/displaying-bitmaps/manage-memory.html – Karakuri Jun 23 '14 at 14:56
  • http://stackoverflow.com/questions/10314527/caused-by-java-lang-outofmemoryerror-bitmap-size-exceeds-vm-budget/10408877#10408877 look at this. – N-JOY Jun 23 '14 at 15:03

3 Answers3

3

Since you are loading your Bitmaps from a server, you should probably use an image loading library.

Powerful libraries are for example:

They allow you to do pretty much anything with your Bitmap and during loading. Chaching is also supported.

UniversalImageLoader is slightly more powerful, Picasso is easier to use, in my opinion.

Philipp Jahoda
  • 50,880
  • 24
  • 180
  • 187
  • I just realized that I posted the wrong code, I edited my original comment to reflect this, but the Drawables aren't in the resources folder, they're stored on the server. I'm not sure if your solution still applies. – shadowarcher Jun 23 '14 at 15:32
  • No, in that case it doesn't apply. You should probably go ahead and use a image loading library. The most powerful ones are probably the universal image loader and picasso. I updated my answer. – Philipp Jahoda Jun 23 '14 at 15:53
2

Bitmaps in android are a bit tricky.. My first app which required a large number of images in a gridview - I ran into a lot of OOM problems as well.

I ended up using nostra13's "Universal Image Loader" as it seemed to be the best solution for what I needed. It has a lot of built in features such as disk cache, memory cache, bitmap size, thread pool size, image scaling, etc. There are working examples too. :)

Nostra13 Universal Image Loader

There are a few things to keep in mind:

  • Recycle your bitmaps before displaying the next one else they will pile up in memory and you'll get OOM
  • If using Universal Image Loader, make sure you use .bitmapConfig(Bitmap.Config.RGB_565) as it uses the least amount of memory per image.
  • If you plan on displaying a lot of images in a gridview or listview, the approach I used is to load two different images from your APIs, one being extremely small (100x100 ish) for thumbnail view, and the other being the full size image. This way you wont run out of memory when showing the thumbnails. Then only when the user clicks a thumbnail, will it load the the full size image for that position.

Hopefully this helps. :)

Jonny07
  • 625
  • 3
  • 14
0

Every time you create new Drawable and BitmapDrawable use one Drawable and refresh image on it.

QArea
  • 4,955
  • 1
  • 12
  • 22