bitmap.copy() throws out of memory error
Asked Answered
P

4

9

I am using universal-image-loader library to load images, but when I call copy() on a loaded bitmap file in some cases I get OutOfMemoryError. Here is my code:

    ImageLoader.getInstance().loadImage(path, new ImageLoadingListener() {

        @Override
        public void onLoadingStarted(String arg0, View arg1) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onLoadingFailed(String arg0, View arg1, FailReason arg2) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) {
            bm = arg2;
        }

        @Override
        public void onLoadingCancelled(String arg0, View arg1) {
            // TODO Auto-generated method stub

        }
    });
 Bitmap bm2= bm.copy(Bitmap.Config.ARGB_8888, true); //where the crash happens

I need the second Bitmap not to be mutable so I can draw on it.

Paronym answered 20/5, 2014 at 15:1 Comment(4)
Those cases might be when images are large. How big are your images in size?Nazario
Also, are you sure you are not keeping other bitmaps in memory while this is not useful? (this often happens) - This doc can also be helpful: developer.android.com/training/displaying-bitmaps/…Nessy
well i am using the image loader to load the images I never had any issues with it. the problem accures only when calling copy() on the bitmap.Paronym
@Paronym btw, why make a copy?Mcdonald
N
15

First of all try to find a little time to read good official documentation about bitmaps: Displaying Bitmaps Efficiently

It will give you understanding why and when java.lang.OutofMemoryError happens. And how to avoid it.

What about your question: see this article: Android: convert Immutable Bitmap into Mutable

But from API Level 11 only options.inMutable available to load the file into a mutable bitmap.

So, if we are building application with API level less than 11, then we have to find some other alternatives.

One alternative is creating another bitmap by copying the source

bitmap. mBitmap = mBitmap.copy(ARGB_8888 ,true);

But the will throw OutOfMemoryException if the source file is big. Actually incase if we want to edit an original file, then we will face this issue. We should be able to load at-least image into memory, but most we can not allocate another copy into memory.

So, we have to save the decoded bytes into some where and clear existing bitmap, then create a new mutable bitmap and load back the saved bytes into bitmap again. Even to copy bytes we cannot create another ByteBuffer inside the memory. In that case need to use MappedByteBuffer that will allocate bytes inside a disk file.

Following code would explain clearly:

//this is the file going to use temporally to save the bytes. 

File file = new File("/mnt/sdcard/sample/temp.txt");
file.getParentFile().mkdirs();

//Open an RandomAccessFile
/*Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
into AndroidManifest.xml file*/
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); 

// get the width and height of the source bitmap.
int width = bitmap.getWidth();
int height = bitmap.getHeight();

//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width*height*4);
bitmap.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
bitmap.recycle();
//Create a new bitmap to load the bitmap again.
bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
map.position(0);
//load it back from temporary 
bitmap.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();

And here is sample code.

Nosedive answered 4/6, 2014 at 10:18 Comment(2)
I still get an OutOfMemoryError using this solution. It happens on this line: bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);Klapp
bitmap = Bitmap.createScaledBitmap(originalBitmap, width, height, true);Dang
T
3

You cant do much about the bitmap outofmemory error except ensuring that the bitmap you are copying or displaying is not much large. Fortunately universal imageloader has a feature to compress bitmap by changing the config. So give Bitmap.Config.RGG_565 a try. Its supposed to half the memory footprint of the bitmap. You can also request for large heap size. Another thing you can do is copy the scaled version of the bitmap.

Two answered 20/5, 2014 at 15:8 Comment(0)
P
3

Be thankful that it happens on your device, and not only on your user's devices.

1) That's something you have to cope with, and react to appropriately. Display error message, or load lower resolution of the bitmap. Your app will run on many kinds of devices, each has different amount of memory.

2) Use important function Bitmap.recycle after each operation which makes your old bitmap redundant. This will immediately free memory for next work without waiting for GC to run, and possible out of memory errors.

Pusan answered 3/6, 2014 at 20:30 Comment(1)
Yes, this is good advice, but remember: You should use recycle() only when you are sure that the bitmap is no longer being used. If you call recycle() and later attempt to draw the bitmap, you will get the error: "Canvas: trying to use a recycled bitmap".Nosedive
C
2

As Illegel Argument said, you need to make sure that the Bitmap is not TOO large. Also, make sure that you are only loading one Bitmap at a time into memory.

You can dynamically scale the bitmap using BitmapFactory

Bitmap b = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length)
image.setImageBitmap(Bitmap.createScaledBitmap(b, 300, 300, false));
Cabriole answered 20/5, 2014 at 15:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.