Android - Bitmap cache takes a lot of memory

2020-01-26 07:41发布

问题:

I'm new to all the memory management subject, so there are a lot of things I don't understand.
I'm trying to cache an image in my app, but I'm having troubles with its memory consumption:

All of the Bitmap Chaching code is pretty much copy-pasted from here: http://developer.android.com/training/displaying-bitmaps/index.html

I debugged the code and checked the heap size in the DDMS view in eclipse, and there is about 15mb jump after these code lines:

        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);

in the "decodeSampledBitmapFromResource" method.

The image is 1024x800, 75kb jpg file. According to what I've already seen on the internet, the amount of memory this image is supposed to take is about 1024*800*4(Bytes per pixel)=3.125mb

All of the threads regarding this subject don't say why it's taking much more memory than it should. Is there a way to cache one image with a reasonable amount of memory?

EDIT

I tried using the decodeFile method suggested on @ArshadParwez's answer below. Using this method, after the BitmapFactory.decodeStream method the memory is increased by only 3.5mb - problem solved, sort of, but I want to cache bitmaps directly from the resource.

I noticed that during the decodeResource method there are 2 memory "jumps" - one of about 3.5mb - which is reasonable, and another strange one of 14mb. What are those 14mb used for and why does this happen?

回答1:

Images are also scaled according to the density so they can use a lot of memory.

For example, if the image file is in the drawable folder (which is mdpi density) and you run it on an xhdpi device, both the width and the height would double. Maybe this link could help you, or this one.

So in your example the bytes the image file would take are :

(1024*2)*(800*2)*4 = 13,107,200 bytes.

It would be even worse if you ran it on an xxhdpi device (like the HTC one and Galaxy S4) .

What can you do? Either put the image file in the correct density folder (drawable-xhdpi or drawable-xxhdpi) or put it in drawable-nodpi (or in the assets folder) and downscale the image according to your needs.

BTW you don't have to set options.inJustDecodeBounds = false since it's the default behavior. In fact you can set null for the bitmap options.

About down scaling you can use either google's way or my way each has its own advantages and disadvantages.

About caching there are many ways to do it. The most common one is LRU cache. There is also an alternative I've created recently (link here or here) that allows you to cache a lot more images and avoid having OOM but it gives you a lot of responsibility.



回答2:

You can use this method to pass the image and get a bitmap out of it :

public Bitmap decodeFile(File f) {
    Bitmap b = null;
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        BitmapFactory.decodeStream(fis, null, o);
        fis.close();
        int IMAGE_MAX_SIZE = 1000;
        int scale = 1;
        if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
            scale = (int) Math.pow(
                    2,
                    (int) Math.round(Math.log(IMAGE_MAX_SIZE
                            / (double) Math.max(o.outHeight, o.outWidth))
                            / Math.log(0.5)));
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        b = BitmapFactory.decodeStream(fis, null, o2);
        fis.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return b;
}


回答3:

@Ori Wasserman: As per your request I used a method to get images from the resource folder and that too I used a 7 MB image. I put the 7 MB image in the "res->drawable" folder and with the following code it didn't crash and the image was shown in the imageview:

 Bitmap image = BitmapFactory.decodeResource(getResources(), R.drawable.image_7mb);
 loBitmap = Bitmap.createScaledBitmap(image, width_of_screen , height_of_screen, true);
 imageview.setImageBitmap(loBitmap);