java.lang.OutOfMemory with large high-resolution images - android

I'm developing an Android app which uses multiple large images in several of its activities. Each image is around 1280x800 and I load about 2-4 of these images for each Activity. I realize that these images are very large in terms of the memory that is allocated to each individual app on a device, but how can I display them at their original resolution without running into a java.lang.OutOfMemory error? I need these images to be displayed at their full size on a screen (scaling is done automatically by xml when the screen is smaller than the image). I saw several solutions involving shrinking down images into thumbnails and storing those into memory, but wouldn't that cause the image to lose it's original size/resolution? Thanks for your help!

There are a few things you can do about this.
The first thing that comes to mind is that 1280x800 is (probably) your entire screen, so you should only need to display one at a time. Don't hold the others in memory while you do that.
Still a 1280x800 image at 4 bytes per pixel is only 4MB, and tablets seem to all offer 48MB of heap these days. You should be able to hold a few in memory if you need to. If you're running out of memory, it's possible you're leaking. If you watch in DDMS, does your memory usage continue to grow as you change activities?
A common source of leaks is the bitmaps themselves. Be sure to call Bitmap#recycle when you're done with them.
If it really comes down to it, and you cannot fit into the heap space provided, you can also try adding android:largeHeap="true" to the application tag in your manifest. This will request the system offer you more heap space - up to 256MB on some devices. This should be a last resort, though, as it will vary by device, and be completely ignored on some (the original Kindle Fire comes to mind).
You can see just how much total heap space you have with Runtime.getRuntime().maxMemory();. See this answer for a more detailed explanation. It's trickier to see just how much you're using, but there is a description here if you want to tackle that beast.
Finally, there may be a better way to load your images than specifying them in xml. Be sure to read this developer guide page. Even if you must leave them in xml, I have seen marked improvement in memory usage by splitting the image assets into the drawable-hdpi, drawable-mdpi, etc. directories rather than just dumping them in drawable.

This article describes pretty well how to create a heap dump and analyze it using Eclipse MAT. This will help you find the most likely suspects for memory leaks pretty quickly.
Again I point you to this great link I found from another SO Question that has tutorials of how to properly over come the problem.

Image needs to scaled before loading the image using BitmapFactory or related methods.
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
whole thing is explained in Android developer site, Loading Large Bitmaps Efficiently

Related

How to create the bitmap and decode it at the same time

I am creating a bitmap and it takes about 11 mb in the heap , though it is of the small size. Well I wanted to know if I can create the bitmap and also sclae it as a same time. The reason I want to do it is , the memory allocation , as If I understand correctly from different bitmap questions which are posted here , and that is
The bitmap allocates the memory as when it is created
So if its , then scaling it again take some process time and also increase the heap size until and unless the garbage collection is not occurred
So what I am doing is
screenHeight = displaymetrics.heightPixels;
screenWidth = displaymetrics.widthPixels;
float aspectRatio = screenWidth / screenHeight;
int modifiedScreenHeight = 400;
int modifiedScreenWidth = (int) (modifiedScreenHeight * aspectRatio);
mBitmap = Bitmap.createBitmap(modifiedScreenWidth, modifiedScreenHeight, Bitmap.Config.ARGB_8888);
So now it is creating the bitmap and allocation the memory , by memory analyzer tool in android studio I can see that it took 11mb in memory.
But I want to minimize them ,I have visited a link and I want to do some more scaling by options as show in this video . but it uses the file to decode such as
BitmapFactory.decodeFile(??,options);
where as I have no file to decode from , I want to decode it from the bitmap I created and to wash away the last created bitmap to clear the memory.
Or if it is possible to set the options when creating it so that we can avoid from extra memory allocation .
Please help.
You can use this using BitmapFactory.Options - specifically, use the options to decode the width / height of the bitmap, then sampleSize to determine how large the generated bitmap will be.
According to your example, you'd like the width/height of the bitmap to be 400 by 400 * aspectRatio. So, first, you'll need to see how large the bitmap needs to be. Do this as so:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(??, options);
int bitmapWidth = options.outWidth;
int bitmapHeight = options.outHeight;
This action will only decode the bitmaps size, without actually allocating memory for the bitmap's pixels. This is good because it's a very quick and light operation which doesn't require much resources and helps you make a more educated decision when loading the bitmap. Now we must use these size to determine how big the generated bitmap will be.
int sampleSize = 1;
while (bitmapWidth / sampleSize > 400 && bitmapHieght / sampleSize > 400 * aspectRatio)
sampleSize *= 2;
sampleSize must be a power of 2 for this to work, and what it will do is determine how many pixels to "skip" when reading the bitmap into memory. This algorithm will set a sample size to a size equal to 1st sample size which will produce a bitmap immediately smaller than the required bounds. You can tweak this if you'd like a slightly different implementation.
Now that you have the sample size, set it with in the options object and load the actual bitmap:
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
Bitmap bitmap = BitmapFactory.decodeFile(??, options);
The generated bitmap will be smaller than the required bounds, thus limiting your memory requirements for creating the bitmap object.
Hope this helps.

Android bitmap size reduction not reducing memory

I'm new to android and am working on my first real app so i apologize if this question has been answered, or isn't detailed enough. I'm having the common OutOfMemoryError when loading bitmaps to my android app. I've searched around on stackoverflow and went through the google tutorial that describes how to use InSampleSize to reduce the memory for each bitmap.
When testing out the app to see how much memory I was saving by down scaling my bitmaps I noticed that the heap size was still growing at the same rate. I.E. When I use an InSampleSize = 8 vs not scaling the bitmap at all (or InSampleSize = 1) the heap grows the same for each.
I have tried printing the byteCount with:
myImage.getByteCount()
for both of the scaling SampleSizes listed above and they both have identical ByteCounts. Is this the expected behavior? Shouldn't the byte count be reduced since I'm downscaling images?
I was expecting to see the memory used to the bitmap reduced by a factor of 8 or so. Am I missing something, or is my understanding of image scaling incorrect? Thanks.
edit: After doing some testing I discovered that if I used createScaledBitmap the memory was reduced but it required me first inflating the original Bitmap then scaling. I'm assuming this is non-ideal since it requires that. I thought the first method would do this for me but according to my heap dumps it isn't.
Initial Code and heap dump:
private static Bitmap decodeSampledBitmap(Resources res, int resId, int width, int height
{
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Config.ARGB_8888;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, width, height);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Updated code (same as above with the only difference being) and heap dump:
private static Bitmap decodeSampledBitmap(Resource res, int ResId, int width, int height)
{
....
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, options);
return Bitmap.createScaledBitmap(bitmap, options.outWidth/options.inSampleSize, options.outHeight/options.inSampleSize, false);
}
It won't let me post images due to my reputation but according to heap dump it is using 79.155MB. Using the second technique the heap dump is using 26.912 MB.
Are you scaling the Bitmap after decoding it using InSampleSize?

Bitmap too large to be uploaded into a texture in some phones

I have image with 543*6423 resolution, I want to display it in all devices. It is displaying in few of android phones which allows the high resolution. I tried with
android:hardwareAccelerated="false"
This is my java code for android client
File storagePath =Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS+
"abc.png");
InputStream is = this.getContentResolver().openInputStream(Uri.fromFile(storagePath));
Bitmap b = BitmapFactory.decodeStream(is, null, null);
is.close();
image.setImageBitmap(b);
This worked in my mobile (sony xperia) but few other phones are not displaying it.
Please help me how can I display this image independent of screen resolution.
Thanks,
Aman
Your image is probably too large to be displayed on most devices. So you have to load a scaled down version of the image. Look at Loading Large Bitmaps Efficiently to see how to do this and calculate an appropriate sample size.
If you need the display width / height (in case of a full screen image) you can use getResources().getDisplayMetrics().
try with
android:hardwareAccelerated="false"
android:largeHeap="true"
Well, maybe I am too late to help you but this can hopefully help others. I've recently developed an open source library that can handle large image loading. The source code and samples are available at https://github.com/diegocarloslima/ByakuGallery
The solution in the blog is wrong by a power of 2...
Here is the solution:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// Set height and width in options, does not return an image and no resource taken
BitmapFactory.decodeStream(imagefile, null, options);
int pow = 0;
while (options.outHeight >> pow > reqHeight || options.outWidth >> pow > reqWidth)
pow += 1;
options.inSampleSize = 1 << pow;
options.inJustDecodeBounds = false;
image = BitmapFactory.decodeStream(imagefile, null, options);
The image will be scaled down at the size of reqHeight and reqWidth. As I understand inSampleSize only take in a power of 2 values.
Instead of spending hours upon hours trying to write and debug all this downsampling code manually, why not try using Picasso? It is a popular image loading library and was made for dealing with bitmaps of all types and/or sizes.
I have used this single line of code to remove my "bitmap too large...." problem:
Picasso.with(image.getContext()).load(storagePath).fit().centerCrop().into(image);

OOM issue android

How to solve OOM issue in android . I have tried almost every things like scaling bitmap,inPurgeable in BitmapOption,releasing all resources etc. but still getting OOM issue.
This is basically in images taken by camera or any image i.e. larger then 1.5 mb. I have also images 15-20 mb size in my app.
this is what i´m doing to avoid OOM errors, Using some of the code of the android training. This is in my class "ScaledFactor"
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inPurgeable = true; // I aded this to the android training code, without this I still have OOM errors. so try using inPurgeable = true
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
In my Activity I use
background = ScaledFactor.decodeSampledBitmapFromResource(getResources(), R.drawable.menu, screenheight, screenwidth); // screeenhieght is the output height , screenwidth is the output width
Then in the on destroy method, or after calling an other intent I use background.recycle();
I´m not using the hdpi, ldpi and so folders... I just use the drawable with large bitmaps, and do the scalling. this way you save some mb on the final apk file
The android training code is here for more info http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap
C ya ! Hope this helps, I spent a hole day trying to figure this out and reading all the questions and answers in this forum. This is just the example of the background image but i have more than 20 images in my game all loaded this way but with smaller output size, and it works very smooth.
Have you tried Bitmap.recycle(); ?
It once solved my Out of Memory issue.

android - out of memory exception when creating bitmap

This question already has answers here:
Strange OutOfMemory issue while loading an image to a Bitmap object
(44 answers)
Closed yesterday.
I am getting the following error after creating bitmap second time around:
04-17 18:28:09.310: ERROR/AndroidRuntime(3458): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
this._profileBitmap = Bitmap.createBitmap(_profileBitmap, xCoor, yCoor, width, height);
From log:
04-17 18:27:57.500: INFO/CameraCropView(3458): Original Photo Size: W 1536 x H 2048
04-17 18:28:06.170: INFO/CameraCropView(3458): xCoor: 291
04-17 18:28:06.170: INFO/CameraCropView(3458): yCoor: 430
04-17 18:28:06.170: INFO/CameraCropView(3458): Width: 952
04-17 18:28:06.170: INFO/CameraCropView(3458): Height: 952
Since the image is huge I get the error. But the interesting thing is the error does not happen the first time, only when I take the picture the second time, which makes me believe this profileBitmap is NOT destroyed. How do I clean this up?
I had the same problem and fix it this way:
My app was ~18MB size, and when I see how much memory left free I was shocked - 654KB (on 1GB RAM!). So I just deleted almost all images from project and downloaded them from the internet on first start, and use pics from SD card when needed.
To check total/free memory for your app use:
Runtime.getRuntime().totalMemory();
Runtime.getRuntime().freeMemory();
EDIT: I forgot the main thing - add in your manifest, between application tag, this line:
android:largeHeap="true"
There are many problems with memory exceptions with bitmaps on Android, many of which are discussed on stackoverflow. It would probably be best if you went through the existing questions to see if yours matches one of the existing ones, then if not, write up what makes your situation different.
Some examples:
Out of memory exception due to large bitmap size
Android: out of memory exception in Gallery
Android handling out of memory exception on image processing
etc:
https://stackoverflow.com/search?q=android+out+of+memory+exception+bitmap
I have explained it in this blog post: android bitmap processing tips
Now here are tips which you can follow and can avoid out of memory exception in your Android Application.
Always use Activity context instead of Application context. because Application context cannot be garbage collected. And release resources as your activity finishes. (life cycle of object should be
same as of activity).
2 . When Activity finishes. Check HEAP DUMP (memory analysis tool in Android studio).
If there are objects in HEAP DUMP from finished activity there is memory leak. review your
code and identify what is causing memory leak.
Always use inSampleSize
Now what is inSampleSize ?
with the help of inSampleSize you are actually telling the decoder not to grab every pixel in memory, instead sub sample image.
This will cause less number of pixels to be loaded in memory than the original image. you can tell decoder to grab every 4th pixel or every second pixel from original image.
if inSampleSize is 4. decoder will return an Image that is 1/16 the number of pixels in original image.
so how much memory you have saved ? calculate :)
Read Bitmap Dimensions before loading into memory.
How reading bitmap dimensions before loading image into memory can help you avoid out of
memory error ? Let's Learn
use inJustBounds = true
here is technique with the help of which you can get image dimension beore loading it in memory
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Above code snippet will not give us any image/bitmap. it will return null for bitmap Object.
but it will definitely return width and height of that image. which is R.id.myimage.
Now you have width and height of Image. you can scale up or scale down image based on these factors:
ImageView size which will be used to display Image.
Available amount of memory. you can check available amount of memory using ActivityManager and getMemoryClass.
Screen size and density of device.
Use appropriate Bitmap Configuration
Bitmap configurations is color space/color depth of an Image. Default bitmap Configuration in Android is RGB_8888 which is 4 bytes per pixel.
If you use RGB_565 color channel which use 2 Bytes per pixel. half the memory allocation for same resolution :)
Use inBitmap property for recycling purpose.
Do not make static Drawable Object as it cannot be garbage collected.
Request large heap in in manifest file.
Use multiple processes if you are doing lot of image processing(memory intensive task) or use NDK (Native Development using c, c++)
You can try calling recycle() on the bitmap when you are done with it. This will clear all the image data and free up the memory. If anything tries to draw the bitmap after this then your app will crash. If you do get a crash it may help you find out what is still holding onto your bitmap.
This happens because you are loading the bitmap directly,which consumes a lot of memory.
Instead use a scaled down version of the picture in _profileBitmap.
This guy explains it pretty well.
http://androidcocktail.blogspot.in/2012/05/solving-bitmap-size-exceeds-vm-budget.html
With Larger images it can be avoided by sampling them into smaller size.
Use below example -
File f = new File(selectedImagePath);
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, 720, 1280); //My device pixel resolution
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bmpPic = BitmapFactory.decodeStream(new FileInputStream(f), null, options);
Bitmap bmpPic1 = Bitmap.createBitmap(bmpPic, 0, 0, bmpPic.getWidth(), bmpPic.getHeight(), mat, true);
img.setImageBitmap(bmpPic1); //img is your ImageView
Reference-
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
You could use a vector Drawable . It uses an xml file to describe your image , so it consumes less memory.
To do that you should use the SVG format for your images and then generate the xml file using one of these 2 solutions :
Solution 1 : Use the vector asset studio in Android Studio : right click on Drawable file in your project -> new -> vector asset
Solution 2 : Use the svg2android website : https://inloop.github.io/svg2android
Check out this link for further information:
https://developer.android.com/studio/write/vector-asset-studio.html
I had the same issue when the phone was powered off and back on. Simply setting the bitmaps to null and calling System.gc(); fixed all the problems.
I had this issue because I was modifying a bitmap once, and then modifying the modified version a second time, resulting in three versions of the same bitmap (original, plus the two modified versions) being in memory at the same time.
I fixed it by changing my image-editing code to apply both modifications to the same bitmap as a kind of batch process, halving the number of modified versions that my app had to hold in memory.

Categories

Resources