简体   繁体   中英

Android Bitmap OutOfMemory issue

My application would have more than 350 images which would be decoded from database. I create bitmap from image data and scale them based on device screen resolution. When I tried to hold all of these bitmaps into memory, I was facing outOfMemory exception. Then BitmapFactory.Options.inPurgeable has been recommended in various places as a way to avoid OutOfMemoryExceptions.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inInputShareable = true;

Bitmap bitmap = BitmapFactory.decodeByteArray(imagaeData, 0, size, options);
...
..
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, true); 

I am caching this scaled bitmap to HashMap and using it for image view. Again I am facing OutOfMemory exception while loading the bitmaps to memory. I don't understnad whether the inPurgeable is working in my case. I am wondering will the scaled bitmap have reference to bytes array. As I am using scaled bitmap, will it have the effect of the inPurgeable option used in decodeByteArray. I am not able to figure out how to handle this bitmap memory issue. Appreciate your help.

350 images are quite a lot. Are you sure that you need them all at once?

Also: as you create a scaled bitmap, you have them in memory twice -> 700 images in memory is way way way too much. You should check if it would be better to use inScale on the option to reduce it to 350 again plus reduce the memory footprint.

I still think that even in optimized ways 350 images are just too much. You should consider a lazy loading solution.

You may try BitmapFactory.Options.inScaled & BitmapFactory.Options. inScreenDensity to get the scaled bitmap.

And you need a better way to cache bitmap in memory. You'd better hold WeakReference of the Bitmap in HashMap for the bitmap, and you can switch to LinkedHashMap for a simple LRU cache implementation.

You don't really need cache all the images, since they'll never get a chance to be displayed in one screen.

Wow, it sounds crazy to try and keep 350 bitmaps in memory at once. Even if they were small I wouldn't recommend it. Surely you won't be able to show all these bitmaps at once, so whats the point of keeping all of them in memory at the same time?

You really should look into using something like Square's Picasso lib for handling image loading and scaling. Picasso handles "ImageView recycling and download cancelation in an adapter", "automatic memory and disk caching" and finally also "complex image transformations with minimal memory use".

Use this method to reduce the image size first (file points to a photo on SD card)

//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
    try {
        //decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        FileInputStream stream1=new FileInputStream(f);
        BitmapFactory.decodeStream(stream1,null,o);
        stream1.close();

        //Find the correct scale value. It should be the power of 2.
        // maximum size is 50
        final int REQUIRED_SIZE=40;
        int width_tmp=o.outWidth, height_tmp=o.outHeight;
        int scale=1;
        while(true){
            if(width_tmp/2<=REQUIRED_SIZE || height_tmp/2<=REQUIRED_SIZE)
                break;
            width_tmp/=2;
            height_tmp/=2;
            scale*=2;
        }

        //decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize=scale;
        FileInputStream stream2=new FileInputStream(f);
        Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
        stream2.close();
        return bitmap;
    } catch (FileNotFoundException e) {
    } 
    catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

// Here is how to call the above method

                    String path = "/mnt/sdcard/DCIM/camera/IMG_2001.jpg";
                    Drawable background = hash_map.get(path);
                    if (background == null) {
                    try {
                        Bitmap bitmap = decodeFile(new File(path));
                        background = new BitmapDrawable(bitmap);
                        if (hash_map.size() > 600) {
                            // to prevent HashMap from growing too large.
                            hash_map.clear();
                        }
                        hash_map.put(path, background);
                    } catch (Throwable e) {
                        // in case there is an exception, like running out of memory.
                        if (e instanceof OutOfMemoryError) {
                            hash_map.clear();
                        }
                    }
                 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM