Decoding Bitmap to ImageView without caching, preventing memory leaks - android

So I've read all tutorials about Bitmap on http://developer.android.com/training/displaying-bitmaps/index.html and it seems they manage them caching with classes at a very specific level. I also know there are libraries like Universal Image Loader that takes care of that for you.
But let's go simple, what if I just want to decode a single Bitmap from a file and place it on an ImageView. As far as I know, to prevent memory leaks you're not supposed to keep a reference to a Bitmap so how can this be achieved. Suppose the bitmap's file path is stored in the following string: imagePath.

Its ok to hold a reference to the Bitmap object provided that you clean up once the Bitmap is no longer needed (e.g. when the activity is destroyed).
To ensure that you do not have any Bitmap-related leaks leaks:
imageView.setImageBitmap(null);
bitmap.recycle(); // frees the Bitmap instance
bitmap = null;

Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath()); //File file
iv.setImageBitmap(bitmap); // ImageView iv

Related

Android select an image from the gallery and resize it before I pull it in

I'm running into issues with the images in my gallery being much much larger than I need them to be.
I've looked for ways to reduce their size before I actually pull them in, but I'm just not quite putting it together as most of what I'm finding deals with BitMap resources and not a BitMap that already exists in the gallery.
So, basically, I am getting the image like so,
imageBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
But before I actually assign it to my BitMap var over there I need to scale it down to a reasonable size for a phone.
Any help with understanding what is going on here better is appreciated.
getBitmap() is a weak convenience method. Its body is a whopping four lines of code:
public static final Bitmap getBitmap(ContentResolver cr, Uri url)
throws FileNotFoundException, IOException {
InputStream input = cr.openInputStream(url);
Bitmap bitmap = BitmapFactory.decodeStream(input);
input.close();
return bitmap;
}
This is why I don't bother teaching people about it.
IMHO, the best solution is for you to use one of the many image-loading libraries available for Android, such as Picasso. Most of the good ones can load from a Uri and handle your resizing as part of the operation, doing the heavy lifting on a background thread.
If, for whatever reason, you want to do all that work yourself, call BitmapFactory.decodeStream() with a BitmapFactory.Options object. In particular, set inSampleSize to indicate that you want the image to be resampled as part of reading it, so you wind up with a smaller Bitmap taking up less heap space.

Dealy in wallpaperManager.setBitmap (bitmap)

I am trying to change wallpaper using a widget.
I use the code below to achieve that:
But there is an certain delay every time the code is executed.
Is there any way to avoid this delay.
...
WallpaperManager wallpaperManager = WallpaperManager.getInstance (context);
Bitmap bitmap = BitmapFactory.decodeStream (new FileInputStream (file));
//here #file is fetched from a phone storage
wallpaperManager.setBitmap (bitmap);
...
I see four options which might help here:
Reduce the size of the bitmap before saving it to the file
Preload the bitmap (well, if possible)
Do all this asyncronous
Make sure your heap is as empty as possible before loading the bitmap. If android has to free heap while decoding a large bitmap it will slow down noticeably

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

Android: "trying to use a recycled bitmap" error with temporary Bitmaps

My app can load quite large images. In an effort to be memory-conservative, I'm attempting to use a temporary bitmap to load and another for the final image after transformation:
.....
finalBitmap.recycle();
finalBitmap = null;
Bitmap tempBitmap = BitmapFactory.decodeStream(fin, ...);
finalBitmap = Bitmap.createBitmap(tempBitmap, ....);
imgview.setImageBitmap(finalBitmap);
.....
Now, at this point we're done with tempBitmap, which was only needed to transport the decoded Bitmap to the transformation step in createBitmap. So:
.....
tempBitmap.recycle();
tempBitmap = null;
.....
And... it crashes with a "trying to use a recycled bitmap" error specifically because of the recycling of tempBitmap. tempBitmap wasn't displayed and is only used right there.
What's going wrong here? Should I just use "finalBitmap" throughout and rely on createBitmap to manage it (finalBitmap = Bitmap.createBitmap(finalBitmap , ....))? I fail to see what ongoing dependency on tempBitmap there would be that would cause such a failure.
Edit: Yes, the null assignment seems to result in the appropriate, eventual garbage collection, but I'm mystified as to why recycle() on a temp Bitmap is so problematic in this case. I get the impression that createBitmap() is holding a reference to it but why, and for how long?
Straight from the Android documentation:
Returns an immutable bitmap from the specified subset of the source
bitmap. The new bitmap may be the same object as source, or a copy may
have been made.
It seems that the createBitmap functions have the potential to re-use the bitmap that you provided. If that is the case, then you shouldn't recycle the temporary bitmap since your final bitmap is using it. One thing you can do is
if(tempBitmap != finalBitmap) {
tempBitmap.recycle();
}
That should only recycle the tempBitmap when it isn't the same as the finalBitmap. At least that seems to be what the documentation is implying.

Efficient way of using images in android (`Drawable` of `bitmap` )

Image is always making problem for me always :(.
java.lang.OutOfMemoryError: bitmap size exceeds VM budget - android is the main problem even if i use very small image.
Please suggest me some tips in using Image. (link, tutorials, sample projects etc...)
Some of my quires are
Which should i prefer Drawable or bitmap
BitmapFactory.decodeFile or Drawable.createFromPath
BitmapFactory.decodeStream or Drawable.createFromStream
How to retain Drawable and Bitmap while rotating.
Is it a good idea to save or pass Drawable or Bitmap through intent
that all depends on what exactly you want to do with your image. bitmapfactory is for creating bitmaps. and drawable objects are used when you want to draw something :)
what do you want to do with your bitmap after you have created it in the memory?
as for rotating, you should take care of onRestoreInstanceState and onSaveInstanceState. something like this:
private static final String PHOTO_TAKEN = "photo_taken";
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState.getBoolean(PHOTO_TAKEN)) {
// do something if you have your pic here
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean(PHOTO_TAKEN, mPhotoTaken); // saving the state of the image (if the photo is taken or not in this case)
}
as for passing through extras... i bet it's a bad idea as you already getting outofmemory exception.... you can pass a reference to that image, like ID of path or something else.
UPDATE:
in one of the projects i was trying to show the image taken from the cam in an ImageView control ion my activity and got that outofmemory exception. the reason was that it was putting the whole big image into memory. the workaround was quite simple: i decreased the image size:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(mImagePath, options);
To avoid out of memory error we can use BitmapRegionDecoder ( to decode the original image which is larger than maximum texture limit 2048x2048 ) or we can decode it by specifying the coordinates from the original bitmap. (for e.g coordinates of each quarter)

Categories

Resources