Avoiding an OutOfMemory Error using ImageView in Android Studio - android

I'm using RecyclerView and CardView for my layout and want to display a watermark which spans across 4 cards on the screen. I've chopped up the watermark image into 4 separate images, one for each card (each image already has the dimensions I want it to be displayed with). To display the images, I am using ImageView inside of each card's .xml file as such:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/cardview"
android:layout_width="415dp"
android:layout_height="89dp"
android:layout_marginTop="0dp"
android:elevation="100dp"
card_view:cardBackgroundColor="#ffffff">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="#drawable/watermark_card1"
android:scaleType="centerCrop" />
.
.
.
</RelativeLayout>
</android.support.v7.widget.CardView>
This works fine while I am only loading 2 out of the 4 images, but when I load the 3rd one, I get the following error:
java.lang.OutOfMemoryError: Failed to allocate a 210446976 byte allocation with 4194304 free bytes and 101MB until OOM
I believe that this is because the images are all very large (521K, 976K, 611K, and 933K) and I am wondering what I could do to use less memory and avoid the OutOfMemory Error. Any advice would be appreciated, thanks!

you must resize or reduce quality of image. and use image library like volly or picasso. this library can solve variety problem relate with image.

In AndroidManifest.xml file, inside the application tag add the below line
android:largeHeap="true"
But, remember this only increase the heap memory and give you solution. But, it's not the best practice. You should recycle your memory properly.
Refer the links
Strange out of memory issue while loading an image to a Bitmap object
Android: convert Immutable Bitmap into Mutable

You can downsample the image as you create it using BitmapFactory. You would just include the images as assets and get an input stream to them, then create downsampled Bitmap objects. This way, you can decide how large you want the images to be based on the device, screen size, memory available, etc. After you create the Bitmap object, use ImageView.setImageBitmap() to set it into your image view. Below is an example of a downsampling method -
public static Bitmap decodeBitmap(InputStream decodeStream, byte[] decodeArray) throws IOException
{
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = null;
boolean useStream = ((decodeStream != null) ? true : false);
int actualFileSize;
// get the decoded object size without actually decoding and creating the object
options.inJustDecodeBounds = true;
if (useStream)
BitmapFactory.decodeStream(decodeStream, null, options);
else
BitmapFactory.decodeByteArray(decodeArray, 0, decodeArray.length, options);
actualFileSize = options.outWidth * options.outHeight * 4;
if (useStream)
decodeStream.reset(); // reset the data - we assume the underlying input stream implements reset() in a
// way that will not throw an exception in the case where the stream is not marked
if (actualFileSize > MAX_BYTES)
{
options = new BitmapFactory.Options(); // recreate the options to get the defaults back
if (actualFileSize/4 <= MAX_BYTES)
options.inSampleSize = 2; // a sample size of 2 has 1/4th the pixels
else if(actualFileSize/16 <= MAX_BYTES)
options.inSampleSize = 4; // a sample size of 4 has 1/16th the pixels
else
options.inSampleSize = 8; // if it is still too big, attempt a sample size of 8 (1/64th the pixels)
if (useStream)
bitmap = BitmapFactory.decodeStream(decodeStream, null, options);
else
bitmap = BitmapFactory.decodeByteArray(decodeArray, 0, decodeArray.length, options);
}
else
{
if (useStream)
bitmap = BitmapFactory.decodeStream(decodeStream);
else
bitmap = BitmapFactory.decodeByteArray(decodeArray, 0, decodeArray.length, null);
}
return bitmap;
}
Finally, as you're loading your images in code, I have found it helpful to force garbage collections (via System.gc()) before and after each call do decodeStream(). When you are done with the Bitmap objects, dispose of their memory explicitly using Bitmap.recycle(). Memory management with large bitmaps on Android can get a little hairy - there are some other suggestions here -
https://developer.android.com/training/displaying-bitmaps/manage-memory.html
Hope this helps.

OutOfMemory error means your images are large in size, so either you decrease quality of the image or use an image library so that it caches your image and also if it still doesnt help then make largeheap=true in your application tag in manifest file.

To avoid this error you have to follow recommendations from this link:
https://developer.android.com/training/displaying-bitmaps/load-bitmap.html
(and look the next two tutorials).
But the easiest way to do it is use one of popular libraries (as were said in one of the answers) - e.g. Picasso (http://square.github.io/picasso/).

Related

Large bitmaps not loading efficiently from sdcard in Android

By following this link, I have written the following code to show a large image bitmap from sdcard.
try {
InputStream lStreamToImage = context.getContentResolver().openInputStream(Uri.parse(imagePath));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
options.inSampleSize = 8; //Decrease the size of decoded image
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeStream(lStreamToImage, null, options);
} catch(Exception e){}
image.setImageBitmap(bitmap);
But it is not returning the bitmap(I mean it returns null). In logcat it is showing the below message repeatedly
08-02 17:21:04.389: D/skia(19359): --- SkImageDecoder::Factory returned null
If I will comment the options.inJustDecodeBounds line and rerun it, it works fine but slowly. The developer guide link I provided above says to use inJustDecodeBounds to load bitmaps efficiently.
Please tell me where I am doing wrong.
inJustDecodeBounds does not load bitmaps. That's the point of it. It loads the dimensions of the bitmap without loading the actual bitmap so you can do any pre-processing or checking on the bitmap before you actually load it. This is helpful is you, say, were having memory issues and you needed to check if loading a bitmap would crash you program.
The reason your bitmap might be loading slowly is because it's probably very large and SD cards are very slow.
EDIT:
From the documentation:
If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller to query the bitmap without having to allocate the memory for its pixels.
Edit 2:
Looking at your code with the example provided by Google, it looks like you are doing relatively the same thing. The reason it's returning null is possibly your InputStream has been modified in the first decoding and thus not starting at the beginning of the bitmap's memory address (they use a resource ID rather than InputStream.
From the code you supplied here, here's what I've figured. You are ALWAYS setting a sample size to 8 regardless of what the first decoding gives you. The reason Google decodes the first time is to figure out what the actual size of the bitmap is versus what they want. They determine that the bitmap is ZxZ dimensions and they want YxY dimensions, so they calculate the samplesize that they should use from the second decoding. You are not doing this. You are simply retrieving the dimensions of the bitmap and not using them. THEN, you set the sample size to a hard-coded 8, swapping it to a hard-coded ARGB_4444 bitmap, then decoding the full bitmap in to memory. In other words, these three lines are not being used:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(lStreamToImage, null, options);
Setting inJustDecodeBounds merely gives you the bitmap's dimensions without putting the bitmap in to memory. It doesn't make it more efficient. It's meant to allow you to load bitmaps in a smaller memory space if they are too big because you can pre-decide what size it should be without decoding the whole thing).
The reason decoding the bitmap is slow might merely be a CPU thing. Depending on the size of your bitmap, you're loading the bitmap from an InputStream from the SDcard which is a slow operation in itself.

Android. OutOfMemory problems

I require displaying many images in my application. These being jpgs and pngs and i'm loading them inside ImageViews like so:
tile.setImageResource(R.drawable.tile_highlight);
I am currently having OutOfMemory problems (java.lang.OutOfMemoryError: bitmap size exceeds VM budget)
I've searched and found some other posts, they all suggest that you should recycle the bitmap of an ImageView manually, like so: ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle(); which will dump it from memory.
BUT in my case, being that i'm not using setBitmap() to load the images onto the ImageView objects, when i try and run the above code, it returns NullPointerException, more precisely, the method getBitmap() returns null, there is no bitmap ?!?!
Do i need to go back in my code and change the way i load all the images in the ImageViews, and then try with the recycle() method? Or how can i free up the memory so it doesn't crash anymore?
EDIT
I've tried something like so: imageView.setImageResource(-1); in hopes it will remove the image from memory and replace it with ... null or something, but it seems it doesn't help the cause.
It would be helpful if you could post some of your code. Specifically, how you're setting the images of the ImageView objects. If you're not using bitmaps, I would expect that getBitmap() will return null. However, if you're using another sort of Drawable or otherwise to set the image, there's likely a similar route to take that doesn't involve bitmaps.
EDIT:
Alright, give this a shot. You can create a Bitmap from a resource like this:
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Then, use the bitmap like this, considering img is your ImageView:
img.setImageBitmap(bm);
//Do some stuff with it, then when memory gets low:
((BitmapDrawable)img.getDrawable()).getBitmap().recycle();
Of course, this is considering that you're in an activity. If not, you'll have to get a handle on the context and replace getResources() with context.getResources().
If you want to set an image from drawable to ImageView, Do not use ImageView.setImageResource(int resId) directly with drawable id. Instead get Scaled down bitmap(if applicable) and set it to imageView. like this:
this works for me.
iv.setImageBitmap(decodeResource(getResources(), R.drawable.big_image));
private static Bitmap decodeResource(Resources res, int id) {
Bitmap bitmap = null;
BitmapFactory.Options options = new BitmapFactory.Options();
for (options.inSampleSize = 1; options.inSampleSize <= 32; options.inSampleSize++) {
try {
bitmap = BitmapFactory.decodeResource(res, id, options);
Log.d(TAG_LOG, "Decoded successfully for sampleSize " + options.inSampleSize);
break;
} catch (OutOfMemoryError outOfMemoryError) {
// If an OutOfMemoryError occurred, we continue with for loop and next inSampleSize value
Log.e(TAG_LOG, "outOfMemoryError while reading file for sampleSize " + options.inSampleSize
+ " retrying with higher value");
}
}
return bitmap;
}
source: https://codingjunkiesforum.wordpress.com/2014/06/12/outofmemory-due-to-large-bitmap-handling-in-android/
try
((BitmapDrawable)im.getBackground()).getBitmap().recycle();
I think you should display images with lower resolution. This would resolve the OOM problem. You can also read Android Developer Guideline link
OutOfMemory error comes when you dont free bitmap once its being used(may be large size) so to prevent this we should keep in mind of re-sizing,taking weakreferences etc.
but there are good libraries available which is taking care all problem one of them is Picasso. please have a look at-
loading images using Picasso

Getting error: external allocation too large for this process

I am trying to implement the code below converting a image path into a bitmap to display on my activity. I am getting the below error. I have tried a bunch of different solutions but none are working
Strange out of memory issue while loading an image to a Bitmap object
Android: Resize a large bitmap file to scaled output file
OutOfMemoryError: bitmap size exceeds VM budget :- Android
Error:
E/dalvikvm-heap(19252): 12742656-byte external allocation too large for this process.
for(int i = 0; i < numItems; i++) {
File imgFile = new File(photoPaths.get(i));
if(imgFile.exists())
{
images[i] = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
}
You may consider loading them in a more just-in-time kind of approach, or using the inSampleSize option of the bitmap factory (i.e., you'd pass a BitmapFactory.Options in to the factory with inSampleSize set to, ideally, a power of 2). Also make sure you set inPurgeable true.
In the event that you are pulling these images from the MediaStore's ContentProvider, you can also use thumbnails.
Perhaps you can tell us more about your use case so we can better help.
You're trying to load multiple large bitmaps, at least one of which is ~12MB. They're too large. The solutions you posted DO work. Resize your bitmaps to something much smaller, especially as you're just displaying the bitmaps on the screen.

BitmapFactory.Options.inSampleSize not being honored?

I have a photo on disk with dimensions 2560 x 1920. This is often too large to load into memory, so I'm trying to use BitmapFactory.Options.inSampleSize to conserve memory. From the docs:
inSampleSize: If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
This is how I use it:
BitmapFactory.Options optsDownSample = new BitmapFactory.Options();
optsDownSample.inSampleSize = 3;
Bitmap bmp = BitmapFactory.decodeFile(path, optsDownSample);
but the app still sometimes crashes on the last line there, and from logcat I can see it's trying to allocate ~5mb, and I suspect this is because the downsampling is not really being honored.
Anyone else know what could be going on here, am I using inSampleSize incorrectly?
Thanks
I'm also struggling understanding how to use the BitmapFactory.Options, based on all the documentation I've read I believe you are just missing the optsDownSample.inJustDecodeBounds = false; as indicated on the Android Developers site.
Best of lucks!

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