Android ListView showing images results in out of memory - android

Okay,
I have what I think is an odd problem.
I load photos into a list view as the user scroll (meaning if picked up from disk or online, it takes a little time and they "pop in")
1)
I ensure that I only fetch one image online + resize to 1/3 width at a time (a lock) -- this is to ensure not e.g. 5 threads each are converting a .jpg to bitmap at the same time. (Imagine a user scrolling very fast, it would then be conceivable this could happen.)
2)
All images on disk are only 1/3 width
3)
While the bitmaps are scaled up to fit device width, I call recycle and set bitmaps to null when used in the getView. (I am converting bitmaps to a BitmapDrawable object which the listitem imageview uses in a setImageDrawable call)
4)
I reuse view in getView if not null
5)
I load bitmaps like this:
BitmapFactory.Options o = new BitmapFactory.Options();
o.inPurgeable = false; // I do not use this since I resize/scale down images myself later
o.inInputShareable = false;
o.inPreferredConfig = Config.RGB_565;
res.bitmap = BitmapFactory.decodeStream(is, null, o);
6)
I also clean scrap items
public void onMovedToScrapHeap(View view) {
final TextView description = (TextView)view.findViewById(R.id.listitem_news_content);
description.setText("");
final ImageView image = (ImageView)view.findViewById(R.id.listitem_news_image);
image.setImageBitmap(null);
}
I am testing on a Samsung Galaxy II. Do you guys have any ideas on what more I could try? Since I only max need to show 10 items, I would think it should be possible...

Try using widely used library Universal Image Loader https://github.com/nostra13/Android-Universal-Image-Loader
It is simple and straight forward and you will never have to care about decoding images yourself.
Option two: There's a powerful new API for loading image is available from the team of Square http://square.github.io/picasso/

Try this, it worked for me in case of OutOfMemory...using System.gc()
bitmap = null;
imageView.setImageBitmap(null);
System.gc();

Related

How should the GridView/ListView with images be handled to avoit memory leaks, if any?

Scenario:
A Fragment
A gridview which displays images from external-storage/downloads from the web
An Adapter for the gridview which uses an xml layout containing an ImageView and a TextView inside a FrameLayout
Inside getView the adapter downloads the image and sets it as resource for the bitmap
After the user is done with the fragment, the fragment is removed.
My concern:
We all know that android is prone to OoM errors, especially when working with bitmaps, even if when they are scaled down.
When the fragment is removed, like onPause should I parse all the items of the GridView and:
remove callbacks for image drawable
recyle the bitmpa drawable ?
Is this needed to prevent memory leaks ?
If you don't need a full size image, I recommend you to use sampled images.
options.inSampleSize = calculateInSampleSize();
options.inJustDecodeBounds = false;
And I use the following tricky code in my own project on decoding files to bitmaps.
public static Bitmap decodeFile(String pathName, BitmapFactory.Options opts) {
for (int i = 0; i < 5; ++i) {
try {
return BitmapFactory.decodeFile(pathName, opts);
}
catch (OutOfMemoryError e) {
System.gc();// Retry with running gc.
}
}
return BitmapFactory.decodeFile(pathName, opts);
}
As I said, my main concern was related to usage of already scaled down bitmaps, but in the same time handling a lot of memory caching of other images. This could be a problem for instance on big screen devices, where an image can be scaled to 1080px and its footprint in memory is not small. I also had to stack some fragments containing images for future use.
This forced the app to run into out of memory after a while. The fix I applied for now is to allow large memory use by android:largeHeap="true"

Loading Bitmap Efficiently in Android

I have dug insanely deep into this topic and found out a lot of options that makes the bitmap loading easier. But what I want to achieve is a lil far-fetched. I have referred this [video] which helps in loading the bitmaps efficiently. My question is unique cause I am able to load n number of bitmap images without any glitches but the problem is that it loads the SAME image1.
CODE :
for(int i=0;i<10;i++)
{
...
in = httpConn.getInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inJustDecodeBounds = false;
options.inBitmap = image;
options.inSampleSize = 1;
image = BitmapFactory.decodeStream(in,null,options);
item = new RowItem(image, product_name, cost,product_id, dream_status,username); --> item is an object of holder class which has get and set methods
rowItems.add(item); --> this is an arraylist object of type holder class
}
Here I have a loop that loads 10 images from WebService, and I am setting it in a adapter at onPostExecute of an async task. When I loop through the 10 images, what happens to me is that, only the last image(10th image) gets set in the view and it gets repeated for all the 10 images. This is due to the following line :
options.inBitmap = image;
which recycles the bitmap. My question here is that, how can I use the inBitmap function and set all the images ? I mean the logic or loop that would let me set not only the 10th image but all the other images. Any help would be highly appreciated guys.I'll be happy to help if you guys have any queries.
NOTE : I am well aware of the fact that inSampleSize should always be set in powers of 2 but setting it to 2 reduces the size of my image and setting it to 1 is what meets my expectation.
I suggest creating a BitmapPool class to smarten up the way you cache and re-use bitmaps. In your example, this pool could start off with a size of 10, and will cache 10 bitmaps. The pool size can grow or shrink depending on the implementation.
you may request a bitmap from this Pool, via getBitmap(), and
Bitmap obtained via getBitmap() call can be used as "inBitmap" while decoding the image
you may return the bitmap back to the pool once you are done using/drawing its contents via returnBitmap()
You can extend the use of BitmapPool to facilitate other bitmap requests in your app.

How to speed up loading images as small previews?

I have 3 or 4 image paths that I use to load an image so I set it to an imageview. Why does it take long? Or better asking is there a way to make it faster? At the end of the day I am loading to fit an imageview of less than 60 dp hight and width
Uri mainImgeUri = Uri.parse(imagePath);
InputStream imageStream;
try {
imageStream = mActiviy.getContentResolver().openInputStream(mainImgeUri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream, null, options);
mainImageIV.setImageBitmap(yourSelectedImage);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
USE CASE:
What happens is that a user will add 5 images (and he get to choose them from Gallery which is mostly taken by phone camera). He hit save and my app stores the path to them in an sqlite database. Then when the user opens the app again to see them, my app query the db to get the paths to all the images and executes the code above x number of times so all the image views are loaded with the intended images
Take a look at http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
It explains how to calculate the correct inSampleSize based on the required dimensions of the output image. It also explains how to reference large bitmaps without having to load all their pixel data into memory.
The idea is that you resample bigger images and only load the smaller ones into memory making the whole process much more efficient. The example code is accessing a bitmap from resources, but this can easily be modified for your needs.
The important things to look out for in the example are inJustDecodeBounds and calculateInSampleSize.

How to implement an image gallery (local images)

Currently im making an app to show images from SD card. Basicly you make an album and add pictures to it, be it from camera of the MediaStore picker.
I tried implementing 2 methods:
The standard gallery app with a custom BaseAdapter to return a view
A viewpager with custom PagerAdapter
I dont want to display a grid view, so it should go fullscreen right away. After that i want to disable swipe left and right and only listen for clicks.
Atm both methods work in portrait mode. When i switch over to landscape some images just drop
03-20 12:20:56.515: W/OpenGLRenderer(17398): Bitmap too large to be uploaded into a texture
followed by Out of memory errors. Stack overflow if full with problems about the OOM and the gallery, you should recycle the views to make them work because convertView is always null in the getView from BaseAdapter.
So i used a recycler for the views, i limit it to 2 views and portrait mode worked for method 1 (using the gallery). Landscape still gives me the same problem.
For method 2 (viewflipper) it handles the views by
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
Wich is never called btw... However portrait mode works here. Landscape still crashes.
My method for getting the bitmap:
public static Bitmap getBitmap(Context ctx, int imageId, ImageView target) {
String file = getPath(ctx, imageId);
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, bmOptions);
WindowManager mgr = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
int scaleFactor = 1;
if (mgr != null) {
// Get the dimensions of the View
int targetW = mgr.getDefaultDisplay().getWidth();
int targetH = mgr.getDefaultDisplay().getHeight();
Log.d(TAG, "Image width + height=" + targetW + "," + targetH);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
scaleFactor = Math.min(photoW / targetW, photoH / targetH);
} else {
Log.d(TAG, "Target is null");
}
// Get the dimensions of the bitmap
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
logHeap(ImageHelper.class);
Bitmap bm = BitmapFactory.decodeFile(file, bmOptions);
if (target != null) {
target.setImageBitmap(bm);
}
return bm;
}
Works fine, i know i use the window manager to get a screensize but that is because my ImageView is still size(0,0) when i inflate it. Afterwards i call
imgView.setLayoutParams(new Gallery.LayoutParams(
Gallery.LayoutParams.FILL_PARENT,
Gallery.LayoutParams.FILL_PARENT));
imgView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
Nothing seems to work...
Please note that i dont use drawables or any other resources included in the APK.
This gallery should be able to load pictures from SD card or from camera in whatever quality they were taken. Obvously the gallery should be able to handle as much images as there are in the directory.
Could someone please help me out with the following questions?
Is there any way to make the default Gallery go fullscreen right away and block out the grid view? This way i only need an adapter to provide images instead of making my own views. (maybe this solves the OOM crashes)
Is my Bitmap decoding function ok? Do i need to built in some hacks to catch the landscape changes?
Whats the best way to make the kind of gallery i need, using a viewpager or the gallery?
Does anyone have some sample code of a fullscreen gallery that doesnt crash?
This is an issue i faced a few times in the past. Google actually posted an article about this.
The problem is in the inSampleSize that u use to decode the image. Since different devices have different screens and memory sizes (VM Heap Sizes that may go from 12 to 64 or more Mb) you cant decode the images the same way in all of them. For example, an image in a 320x240 device with a 12Mb Heap Memory should u a given inSampleSize while a 1280x720 64Mb Heap Memory device should user another inSampleSize (a bigger one).
The article i attack shows an efficient way to render the images for a given height and width. Take a special attention to the last code segment, they "pre-open" the file and calculate the inSampleSize for a "full-decode" later on.
I would also like to add this recommendations.
Use the "getBitmap" function in an async way, call a new thread or an asyncTask (this is done because using the SD takes time and this will cause your application to hang, to be slow and give a bad impression on low-mid end devices.
Try to always use the same Bitmap object. Avoid creating multiple "Bitmap bitmap = new Bitmap()". You have to realize that loaded bitmaps are stored in ram and once assigned to a view its "harder" for the garbage collector to get them.
When you have finished using a Bitmap over make it null. This of this, imagine your have 12Mb max heap RAM. your app is using 8Mb now. Garbage collector starts and cant get any more unused RAM. you 10 Bitmaps fast never make them null after assigning the data to the ImageView and somehow you retain their reference. Taken that each bitmap take 500Kb of Ram your app will crash. Always make Bitmaps null after using.
Finally use the DDMS provided in the google platform-tools (you can access it with the eclipse to debug and see your memory usage, you can also force Garbage Collection to see how your app behaves).

display huge Images in Android

I'm intending to display very large Images in Android. My first solution - to supply them as pdf - fails because not every handheld got a pdf-viewer preinstalled, and I don't want to require the users to install one.
So I have a png now (width = 3998px height=2827px) that I want to display. I downloaded this image to test how it would be displayed the gallery. It was quite painful. It seems that the galery renders this picture only once, and if I Zoom in, I cannot read the text at all.
So I wrote a testActivity which simply has an ImageView nested in a LinearLayout. I put the image into the drawable and set it as ImageView's image-source.
Unforunately the app crashes immediatly, due to an "
ERROR/AndroidRuntime(8906): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget"
I didn't expect that ONE single Image can be too large forVM's memory. I played a little bit around, set ImageViews size to 3998 & 2827px , put the Image to sdCard and read it manually with a fileInputStream.
To my big surprise it now shows my image, but if I turn my Nexus S horizontal I get the same OutOfMemoryError as before.
Can somewone point me the main difference between recieving a Bitmap through a FileInputStream or to set it as ImageView's source.
Also I'm not able to scroll comfortable with two parent scrollViews
I searching for a simple solution to display ONE large image at a time with the ability to scroll horizontal and vertical while able to zoom in and out.
here is a sample of the image I want to display
I know it's an old post but I spent a lot of time on this problem, so here's my solution.
I wanted to display a 2000×3000 picture but I got out of memory or the image was too large to be displayed.
To begin, I get the dimensions of the picture:
o = new BitmapFactory.Options();
o.inJustDecodeBounds=true;
pictures = BitmapFactory.decodeStream(new FileInputStream(f), null, o);
Then I cut it up into four parts and displayed them with four ImageViews.
I tried to load the full picture and cut it into four (using BitmapFactory.create(bitmap,int,int,int,int)) but got out of memory again.
So I decided to use some BitMapRegionDecoder:
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
ImageView iv = new ImageView(this);
InputStream istream = null;
try {
istream = this.getContentResolver().openInputStream(Uri.fromFile(f));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
BitmapRegionDecoder decoder = null;
try {
decoder = BitmapRegionDecoder.newInstance(istream, false);
} catch (IOException e) {
e.printStackTrace();
}
int nw = (j*width/k);
int nh = (i*height/k);
Bitmap bMap = decoder.decodeRegion(new Rect(nw,nh, (nw+width/k),(nh+height/k)), null);
iv.setImageBitmap(bMap);
}
}
This worked.
I know its an old question but I used TileView to do exactly this:
https://github.com/moagrius/TileView
Try to use this one:
https://github.com/davemorrissey/subsampling-scale-image-view
A custom image view for Android, designed for photo galleries and
displaying huge images (e.g. maps and building plans) without
OutOfMemoryErrors. Includes pinch to zoom, panning, rotation and
animation support, and allows easy extension so you can add your own
overlays and touch event detection.
You can use this "easy to integerate" source of WorldMap application:
https://github.com/johnnylambada/WorldMap
This uses a huge image of a world map, and uses cache to display a map.
To integerate, I just copied all the java files (5 i guess) and used the surfaceView in my layout file. Then I went through the small OnCreate() method of ImageViewerActivity.java and used the code in my activity (with sligh alteration, depending on my personal use).
This post is a good demo for zooming a picture using multi touch gestures. It uses Matrix to zoom as well as pan a picture.
To handle scaling etc and using full resolution, You can use MapView of OSM (open street map) and provide it with your tiles (instead of the map). Check this: http://www.haakseth.com/?p=30
We have to follow following steps to remove out of memory exception while loading huge images:
1. Read Bitmap Dimensions and Type
2. Load a Scaled down version into memory
Android Developer's Guide defines how we achieve these.
here is the link
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html#load-bitmap

Categories

Resources