I'm tring to get image from gallery (by intent).
I got this error:
985120-byte external allocation too large for this process.
Out of memory: Heap Size=4871KB, Allocated=2472KB, Bitmap Size=19677KB
VM won't let us allocate 985120 bytes
That's my code where I get image:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
....
mBitmap = Media.getBitmap(this.getContentResolver(), data.getData());
...
}
How can i solve it ?
-------- UPDATE ---------
I noticed that if I select a pre-existent image (HTC photo installed) I get this error. If I select image picked from camera all works fine.
So, I change my code according to this http://developer.android.com/training/displaying-bitmaps/load-bitmap.html:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream stream = getContentResolver().openInputStream(data.getData());
mBitmap = BitmapFactory.decodeStream(stream,null,options);
stream.close();
But now the bitmap is NULL !!!
It looks like your application uses a lot of high-res bitmap (the bitmap memory partition is 19677KB). The sie of 'heap' and 'allocated' are quite normal, and should not be of any problem. Make sure that you remove unused bitmap from memory. You can free a bitmap from memory by calling bitmap.recycle(), or setting the reference to it to null. Take a look at LruCache if you want to cache bitmaps for performance reasons.
I always wrap decoding in a while loop increasing the inSampleSize and catching OutOfMemoryError. This will give you the maximum possible resolution image. Always use LRU caches!
Bitmap image;
boolean success = false;int counter = 0;
while (success == false && counter < 10)
{
try
{
image = BitmapFactory.decodeFile(photoPath, options);
success = true;
}
catch(OutOfMemoryError e)
{
System.gc();
options.inSampleSize++;
counter++;
}
}
Related
BitmapFactory.decodeFile causes outOfMemoryError only when I choose the image from the phone gallery, not when I choose the image from taking the photo with the camera intent. I actually avoid using decodeFile when using the camera intent. Can I use that same approach when choosing from the gallery (getting data from the intent)? Here is my code for getting the image in both ways:
If image was chosen from the device' gallery:
if (requestCode == 1)
{
if (intent != null && resultcode == RESULT_OK)
{
ImageHelper ih = new ImageHelper();
mProfilePicPath = ih.getSelectedImageFilePathFromGallery(this, intent);
Bitmap portraitPhoto = ih.getChosenImageFromGallery(mProfilePicPath);
try{
ih.saveImage("Profile pics", portraitPhoto, this);
ImageHelper.getChosenImageFromGallery:
public Bitmap getChosenImageFromGallery(String imagePath) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, options);
options.inSampleSize = DressingRoomActivity.calculateInSampleSize(options, 500, 500);
options.inJustDecodeBounds = false;
Bitmap portraitPhoto = ImageHelper.convertToPortraitOrientation(options, imagePath);
return portraitPhoto;
}
If image was chosen by taking photo using camera:
else if ((requestCode == CAMERA_REQUEST)&& (resultcode == Activity.RESULT_OK)){
Bundle extras = intent.getExtras();
Uri selectedImage = intent.getData();
if (extras != null) {
ImageHelper ih = new ImageHelper();
mProfilePicPath = ih.getFilePathFromCameraPhoto(this, selectedImage);
Bitmap photo = (Bitmap) intent.getExtras().get("data");
try{
ih.saveImage("Profile pics", photo, this);
As you can see, to grab the image from the gallery and get the Bitmap from it, I have to create the Bitmap to be only 500 x 500 pixels with this code options.inSampleSize = DressingRoomActivity.calculateInSampleSize(options, 500, 500); Otherwise it causes an outOfMemoryError on the line Bitmap bmp = BitmapFactory.decodeFile(path, options); Which is in the ImageHelper.convertToPortraitOrientation(options, imagePath); method. This means the user gets AS IS quality in their image from the camera, and 500px quality from their image in the gallery.
My questions: 1) Is there any way to not make my image quality 500px when choosing from gallery? Like avoid this line somehow: Bitmap bmp = BitmapFactory.decodeFile(path, options); or some other way?
2) With the gallery I had to convert to portrait which is only a minor annoyance but why is that? It flips the photo sideways if I don't.
Here is a complete tuto on how to Display Bitmaps Efficiently from the official doc. I had the same problem as you. What i did was to read and understand all the lessons from the given page before returning back to my codes. Try that, you will get things clearer in your head.
Edit
Your app is crashing because you are running the Bitmap.decode() method in the main thread. Use another thread (with a simple AsyncTask) to do that.
As i said before, there is a nice tuto on how to handle bitmap off UI Thread
There is 2 possible solutions to overcome outOfMemory exception:
Solution 1: you should use weakReference variables like.
WeakReference<Bitmap> weakReferenceBitmap=new
WeakReference<Bitmap>(bitmap);
GC clear weakReference variable more efficient as compare to other.
Solution2: you can use largeHeap property in your manifest to require more heap space then normal.
<application
android:largeHeap="true">
</application>
I will not recommend you the 2nd solution, but it works :)
Hope it will help :)
My app decodes a lot of bitmaps from sd card, so I want to reuse existing bitmaps to decrease GC work. I see examples from Android Training and this video and it works perfect for BitmapFactory.decodeResource, but not for BitmapFactory.decodeFile. I use next code:
private void testBitmapReusing() {
BitmapFactory.Options options = newOptions();
Bitmap bitmap = decode(options);
options.inBitmap = bitmap;
bitmap = decode(options);
}
private BitmapFactory.Options newOptions() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inMutable = true;
return options;
}
private Bitmap decode(BitmapFactory.Options options) {
return BitmapFactory.decodeFile("/mnt/sdcard/sun.jpg", options);
// return BitmapFactory.decodeResource(getResources(), R.drawable.sun, options);
}
Commented code (BitmapFactory.decodeResource) works as expected, it decodes new bitmap using existing bitmap. But uncommented code (BitmapFactory.decodeFile) doesn't decode new bitmap. It just writes to log message "E/BitmapFactory: Unable to decode stream: java.lang.IllegalArgumentException: Problem decoding into existing bitmap"
So where is my mistake?
UPDATE
I realize my fail. I try to decode GIF images, but it is impossible reuse bitmaps in GIF format. The docs says:
The source content must be in jpeg or png format (whether as a resource or as a stream)
It might not be what you're looking for directly, but the alternate approach would be to create a Map (e.g. a HashMap) and add the resources to that as soon as they're being called. Something like this:
HashMap<String, Bitmap> map = new HashMap<String, Bitmap>();
public Bitmap loadBitmap(String location) {
if (map.containsKey(location))
return map.get(location);
Bitmap bmp = yourDecodeMethod(location);
map.put(location, bmp);
return bmp;
}
This is the same way I use when I do not want to reload resources every time. Sorry if this is not what you wanted to do, I tried my best to understand your actual goal :)
Good luck!
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();
}
}
}
I use the following function to retrieve a contact's photo in Android where the person's lookup key is given:
public Bitmap getContactPhoto(String lookup_key) {
Uri lookUpUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookup_key);
Uri contentUri = ContactsContract.Contacts.lookupContact(ctx.getContentResolver(), lookUpUri);
InputStream stream = null;
try {
stream = ContactsContract.Contacts.openContactPhotoInputStream(ctx.getContentResolver(), contentUri);
}
catch (Exception e) {
}
if (stream != null) {
return BitmapFactory.decodeStream(stream, null, bitmapOptions);
}
else {
return null;
}
}
When I display those contact photos in a list view, I sometimes read the following error in the developer console's crash reports:
Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=6023KB, Allocated=3002KB, Bitmap Size=27152KB)
Does this mean that my app may use about 3MB of heap but a single bitmap had roughly 27MB?
I read a lot of questions here on Stack Overflow concerning OutOfMemory error but it was mainly about:
leaking contexts so that the garbage collector cannot free the resources
huge bitmaps that need to be scaled
But how can I prevent the error in my case? Since I only get the contacts' photos, I don't know if I have huge bitmaps that must be scaled. And leaking a context doesn't seem to be the case here.
This is how the images are displayed:
imageView.setVisibility(View.VISIBLE);
imageView.setBackgroundResource(R.drawable.background);
if (<BITMAP_OBJECT> != null) {
imageView.setImageBitmap(<BITMAP_OBJECT>);
}
else {
imageView.setImageBitmap(null);
}
When you are setting your Bitmap to an ImageView, try doing this:
Bitmap oldBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
imageView.setImageDrawable(null);
oldBitmap.recycle();
imageView.setImageBitmap(newBitmap);
It may not complete get rid of the problem, but it certainly makes it happen much more rarely.
Have you tried scaling your bitmap? Here is how you do it...
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile( filename, options );
options.inJustDecodeBounds = false;
options.inSampleSize = 2; // adjust sample size to whatever you want it to be
bitmap = BitmapFactory.decodeFile( filename, options );
if ( bitmap != null && exact ) {
bitmap = Bitmap.createScaledBitmap( bitmap, width, height, false );
}
Here in the method BitmapFactory.decodeFile, I create a scaled Bitmap so that it consumes lesser memory than it did before, it solved my problem, what about yours?
In my application I load a couple of images from JPEG and PNG files. When I place all those files into assets directory and load it in this way, everything is ok:
InputStream stream = getAssets().open(path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
But when I try to load the exact same images from sd card, I get an OutOfMemory exception!
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
This is what I get in the log:
11-05 00:53:31.003: ERROR/dalvikvm-heap(13183): 827200-byte external allocation too large for this process.
11-05 00:53:31.003: ERROR/GraphicsJNI(13183): VM won't let us allocate 827200 bytes
...
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
...
Why can this happen?
UPDATE: Tried both of these on real device - it seems that I can't load more than 12MB of bitmaps into whatever is called "external memory" (this is not an sd card).
I tried all the approaches mentioned here & at other resources but I came to the inference that setting ImageView's reference to null will solve the issue:
public Bitmap getimage(String path ,ImageView iv)
{
//iv is passed to set it null to remove it from external memory
iv=null;
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
stream=null;
return bitmap;
}
& you are done!
Note:Though it may solve above problem but I would suggest you to check Tom van Zummeren 's optimized image loading.
And also check SoftReference: All SoftReferences pointing to softly reachable objects are guaranteed to be cleared before the VM will throw an OutOfMemoryError.
When doing a lot with bitmaps, don't debug the app - just run it. The debugger will leave memory leaks.
Bitmaps are very expensive. If possible, scale them down on load by creating BitmapFactory.Options and setting inSampleSize to >1.
EDIT: Also, be sure to check your app for memory leaks. Leaking a Bitmap (having static Bitmaps is an excellent way to do that) will quickly exhaust your available memory.
Probably nothing wrong with your API usage, I guess all we can do is infer that using the AssetManager involves less behind-the-scenes heap allocation than opening a random file from the SD card.
800KB is a serious allocation in anybody's book... this will doubtless be for the decompressed image pixels. Given that you know the size of the image, what depth is it? If it's 32bpp then try overriding that using inPreferredConfig.
This is a fairly common issue which all of us face while loading images from the sdcard.
The solution as I found was to use inJustDecodeBounds first while loading the image using decodeFileDescriptor . That would not actually decode the image, but give the image size. Now I can scale it appropriately(using the options) so as to resize the image for the display area. Its needed because low memory on the phone can be easily taken over by your 5MP image. This I believe is the most elegant solution.
There are two issues here....
Bitmap memory isn't in the VM heap but rather in the native heap - see BitmapFactory OOM driving me nuts
Garbage collection for the native heap is lazier than the VM heap - so you need to be quite aggressive about doing bitmap.recycle and bitmap =null every time you go through an Activity's onPause or onDestroy
Instead of loading it from the SD Card directly, why not move the image to the cache in the phone's internal storage using getCacheDir() or use a temp directory to store the images in?
See this, this on external memory usage. Also, this article may be of relevance to you.
Use the below code and you will never get the following error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inSampleSize = 4;
myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds);
picturesView.setImageBitmap(myBitmap);
The best solution i found and edited according to my need
public static Bitmap getImageBitmap(String path) throws IOException{
// Allocate files and objects outside of timingoops
File file = new File(thumbpath);
RandomAccessFile in = new RandomAccessFile(file, "rws");
final FileChannel channel = in.getChannel();
final int fileSize = (int)channel.size();
final byte[] testBytes = new byte[fileSize];
final ByteBuffer buff = ByteBuffer.allocate(fileSize);
final byte[] buffArray = buff.array();
#SuppressWarnings("unused")
final int buffBase = buff.arrayOffset();
// Read from channel into buffer, and batch read from buffer to byte array;
long time1 = System.currentTimeMillis();
channel.position(0);
channel.read(buff);
buff.flip();
buff.get(testBytes);
long time1 = System.currentTimeMillis();
Bitmap bmp = Bitmap_process(buffArray);
long time2 = System.currentTimeMillis();
System.out.println("Time taken to load: " + (time2 - time1) + "ms");
return bmp;
}
public static Bitmap Bitmap_process(byte[] buffArray){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither=false; //Disable Dithering mode
options.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
options.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
options.inTempStorage=new byte[32 * 1024]; //Allocate some temporal memory for decoding
options.inSampleSize=1;
Bitmap imageBitmap = BitmapFactory.decodeByteArray(buffArray, 0, buffArray.length, options);
return imageBitmap;
}
Thanks to all the threads, I've found a solution that works for me on a real device.
The tricks are all about using
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inSampleSize=(int)(target_size/bitmap_size); //if original bitmap is bigger
But for me this was not enough. My original image (taken from the Camera app) was 3264x2448. The correct ratio for me was 3, since i wanted a plain VGA image of 1024x768.
But setting inSampleSize to 3 was not enough: still out of memory exception.
So in the end I opted for a iterative approach: I start from the computed correct size, and increase it until I stop having a OOM exception.
For me it was at sample of 4.
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
// o2.inSampleSize = scale;
float trueScale = o.outWidth / 1024;
o2.inPurgeable = true;
o2.inDither = false;
Bitmap b = null;
do {
o2.inSampleSize = (int) trueScale;
Log.d(TAG, "Scale is " + trueScale);
try {
b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (OutOfMemoryError e) {
Log.e(TAG,"Error decoding image at sampling "+trueScale+", resampling.."+e);
System.gc();
try {
Thread.sleep(50);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
trueScale += 1;
} while (b==null && trueScale < 10);
return b;
You must not depends on the GC to recycle your bitmap memory.
You must clearly recycle the bitmap when it is not needed.
See the Bitmap method:
void recycle()
Free up the memory associated with this bitmap's pixels, and mark the bitmap as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing.
Try this another way...
Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path");
Allows inSampleSize resize the final read image.
getLength() of AssetFileDescriptor allows get size of file.
You can vary inSampleSize according to getLength() to prevent OutOfMemory like this :
private final int MAX_SIZE = 500000;
public Bitmap readBitmap(Uri selectedImage)
{
Bitmap bm = null;
AssetFileDescriptor fileDescriptor = null;
try
{
fileDescriptor = this.getContentResolver().openAssetFileDescriptor(selectedImage,"r");
long size = fileDescriptor.getLength();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) (size / MAX_SIZE);
bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try {
if(fileDescriptor != null) fileDescriptor.close();
} catch (IOException e) {}
}
return bm;
}