I have a long ListView in which there is an ImageView for each row. It displays the correct bitmaps if I slowly scroll the list (each row has different icon to show).
The problem comes up when I scroll the ListView fastly. It happens that many images are not loaded into their ImageView, leaving it transparent. Even the ones that were previously shown scrolling the list slowly.
Here is the code inside the getView() method that should display the icons:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
String name = ... //custom code to get the icon name to show
Bitmap bitmap = BitmapFactory.decodeFile(getIconsDir() + name + getIconsExt(), options);
holder.imgIcon.setImageBitmap(bitmap);
I'm recycling the convertView in the getView() method of the adapter.
I know for sure that the bitmap exists in that location.
imgIcon is the ImageView referenced by the ViewHolder of the row.
I've also tried to use image loaders (like Picasso) but I got the same result.
Does anyone have experienced this before?
Do not do bitmap decoding in adapter's getView(). It will Cause ListView to lag and also fill up RAM very fast.
You should:
Use a size limited, in-memory cache to hold onto bitmaps.
Decode and Load images in Views asynchronously.
ListView re-cycles the views, and device memory may not hold all the bitmaps loaded all the time. Even Image loading Libraries face that issue.
Yes, there are performance issues with images in ListViews.
But prefetching the images in any way is a could approach to avoid the calls to read operations on the file system and decoding of binary data.
I extinguish that you are storing your viewholder in the convertviews - tag.
Loading images from disk is a slow operation, and so is decoding them from whatever compressed format they're stored in (like JPG or PNG) into a Bitmap. So ideally, it shouldn't be done in getView() on the main thread. Also, they take up a lot of memory.
If these "icons" are static, you should just include them as drawable resources and then use ImageView.setImageResource. Then the OS will figure out how to load them most optimally (plus, that way you'll have the correct resolution for the given screen).
If the icons are not static (e.g. loaded from the network), I recommend using an in-memory cache with an asynchronous loader, but make sure it's limited in size and/or uses weak references (but beware: Android generally advises against using weak/soft references, especially for bitmaps, since the memory allocated for bitmaps is in native code, so the garbage collector doesn't know how much memory those images are really taking up and it may decide not to collect them even if it's running low on memory... because it thinks they're small).
Related
I'm having serious issues with memory, and I'm looking forward to recycle bitmaps.
However, what my app is doing atm, is building a Gallery (yeah, the deprecated) with several bitmaps, and then, several Galleries.
So at the end of the day, the app looks like (LinearLayout style):
*Some webviews
*A Gallery with 7 images.
*Some webviews
*A Gallery with 10 images
and so on.
So what I'm thinking is... Once I've displayed those images, and them are on the screen, can those bitmaps be recycled?
Is there any way to recycle a whole Gallery component?
Thank you so much.
Edit:
I've tried soo many things. I'm still getting the error java.lang.IllegalArgumentException: bitmap size exceeds 32bits
This is my actual code:
BitmapFactory.Options bfOptions=new BitmapFactory.Options();
bfOptions.inDither=false; //Disable Dithering mode
bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
bfOptions.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
bfOptions.inTempStorage=new byte[32 * 1024];
bfOptions.inInputShareable=true;
bfOptions.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeFile(mUrls.get(position).getPath(), bfOptions);
inflatedImageView.setImageBitmap(Bitmap.createScaledBitmap(bitmap, 120, 120, false));
As you can see, I've set many options to the Bitmap, aswell as resizing it, and lowering it's quality. Still happenning the same issue.
First you should never manually call system.gc, and generally there is no need to manually recycle bitmaps either. Let the operating system do what its best at, deciding when to take the huge hits to run garbage collection.
One of the reasons that gallery was deprecated is I am fairly sure it just loads all items as soon as it renders. And does not load them and recycle them on demand like a list view. Which means it is a huge waste of RAM and no amount of recycling will help you. In addition Gallery is very glitchy on 4.0+ phones, which is the majority of the Android user base at this point. I would strongly recommend you move away from using gallery.
There are two alternatives.
1) View Pager which is now built into the Support V4 library
2) HorizontalListView --> https://github.com/MeetMe/Android-HorizontalListView
Also be absolutely sure you are downsampling all images prior to loading them in the image views. To learn how read http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Ok. I solved the issue I was having; I had a linearLayout with gallery components and webviews. If I didn't render the webviews the app didn't crash.
So after some tweaks now it's working correctly without crashes:
wv_detail.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
wv_detail.setLayerType(View.LAYER_TYPE_NONE, null);
I am looking for a library or some idea on how I can load an image by parts from the disk or a url straight to the disk then in parts again to the ram. So the two ways I see that this can be done is loading the whole image onto the disk by reading and writing it from the url directly using the ram only for the buffer then when the image is on the disk some how creating bitmaps of only parts of the image, that way I DO NOT load it all and putting those in a ListView.
The issue is that I am dealing with extremely long images (10K + pixels long w/ a width of 4-600) and they hog up lots of ram if loaded all in one bitmap. I can not just scale them down like the Google android tutorial does in the handling large bitmaps section as that results in a width too small to deal with. So if I can somehow generate small bitmap slivers on the disk I can use them by loading them in a ListView preventing loading the image as a whole into ram.
Right now I am breaking the long images into slivers from a bitmap and I realized that that isn't really accomplishing what I am trying to do as the whole image is loaded into a bitmap in memory and is then broken up, then GC (So I am using up the full abmount of ram anyways). I am testing on a new top of the line android phone and the app works fine, but the heap size reaches 80mb+ with the larger images temporarily in ram as it breaks down the bitmap and this will be an issue with devices that have lower heap limits
You can try using this class, support from 2.3 http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html
If you are using Java you can work with InputStreamReader and OutputStreamWriter. Method read() accepts the buffer as one of the parameters, its length can be anything suitable. Of course you can create a new file for each buffer being written.
Is it what you're looking for?
edit
well its not. have you seen this Strange out of memory issue while loading an image to a Bitmap object ?
If you have control over the server from where you are fetching data, throw in another field in your response, such that it returns a thumbnail/smaller image. The server can then generate the required thumbnails for you, without you bothering about it.
Decoding bitmaps on the fly might be expensive, most of the times. If you can't change anything on the server, download and save the images, and after saving, generate their corresponding thumbnails and save them as well. In your list, use the thumbnails. Also, save the information about which images have been cached, and whose thumbnails you already have. This might look like a lot of work, but depending on the use case, this can be a better approach dealing with large images.
Problem with downsizing?
Well, you can come up with some kind of logic as to generate thumbnails, based on the original size of the image. For longer(vertically long images), you could use BitmpapRegionDecoder (http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html) as #Binh Tran has suggested.
Try maybe encoding the image to make the size small.
I'm having an issue with an adapter for a grid view. The adapter causes a bitmap to be loaded every time a particular item is needed. I've got a disk cache and memory cache and I'm disabling loading while scrolling, so generally speaking, the gridview is quick.
But what I'd really like is to eliminate constantly having to rebind a particular Bitmap to an ImageView. Since the gridview is reusing views, the getView method has to keep reseting the ImageView and reloading the image from memory. This creates a really stupid effect as items slide offscreen and when returned to have a delay as the bitmap is read from the memory cache and posted to the ImageView.
Is there a way to get the GridView to 'hold on to' more views instead of being really frugal? I have a maximum of about 20 items and im scaling all the bitmaps, so I'd really like to just hold on to more of the views if possible.
I was able to solve this using a better in memory cache. The real distinction was to add a mechanism to check if the image was in memory and not 'reset' the view if the required image was in memory. It still seems bogus that there is not an easy way to set the number of cached views off screen for grid view, but better memory cache management made the problem less noticeable.
In the API demo , there is an example of an effecient list adapter in which all the bitmap are preloaded in the adapter constructor :
// Icons bound to the rows.
mIcon1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_1);
mIcon2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon48x48_2);
In case the adapter have to deals with a lot of bitmap, is it still a good practice or will it be better to do something like that in the getView :
holder.myImage.setImageDrawable(context.getResources().getDrawable(R.drawable.icon48x48_1));
Note in my case , i can have something like 10-15 possible bitmap , but only a few will be used at same time. I'm concerned by the memory used by the adapter.
Thanks
If the icons are truly 48x48 pixels, they won't be that big, and since you have a known small set of them, preloading them is probably fine. I would not use that approach if:
the images are massive, or
the images are being loaded from places other than resources, or
there would be more than a few dozen icon-sized images
You can always dump your heap from DDMS and inspect it with the Eclipse MAT plug-in to get a perspective on how much heap space is actually being used. Test it on Honeycomb or Ice Cream Sandwich, though, as you get better results for tracking bitmap heap consumption.
One way of doing it is to wait until the list stops and then set the images for the list items that are visible. This way you will only have to load the images that are currently displayed (compared to pre-loading everything in the constructor), and it won't make the scrolling feel choppy or slow. It's a bit more work, but it will make you list really smooth with minimal memory usage.
When displaying a gallery with lots of large bitmaps (fullscreen, wvga), I quite frequently get out of memory issues. I assume this is related to bitmaps not being recycled. When/how can I force bitmaps to be recycled?
I also noticed that in the getView method of simpleCursorAdapter, convertView is always null. I assume this means that the old view is not recycled? Even when scrolling back and forth, a new view is created each time. However, scrolling back and forth does not cause out of memory issues, that only happens when the total number of images is large enough.
When using the same adapter with a ListView, views are recycled, so it seems the problem is with the Gallery.
What can I do to force views and/or bitmaps to be recycled? What else can I do manage memory without reducing gallery size and bitmap quality.
You can use recycle() if you are sure tou won't use a bitmap anymore, for example in any operation with a temporary bitmap. But...You can use BitmapFactory.Options, for ex:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inPurgeable=true;
Bitmap bitmapTemp = BitmapFactory.decodeResource(getResources(), R.drawable.intro, bitmapOptions);
What it does? the option inPurgeable will asume that once the system needs memory and your bitmap it's no more useful, system will recycle this memory allocation by itself. If for some reason you cannot load an image with the BitmapFactory then you can use Recycle() and it's recommendable to call de garbage collector then with system.gc(), this is usefull if you're developing for example a game, because when garbage collector it's activated when you system it's low on memory, it will consume some valious milliseconds.
You should be very careful with bitmaps, they're not stored on the "normal" memory used by your app, it's stored on a "general" memory used by all your apps, unless you are using Android 3.0.
If you get a memory leak and can't find it looking at the heap... you should take a look at your code loading and using bitmaps.
Hope it helps.
You never need to recyle a Bitmap. However, you can do it, and it will help when you have OutOfMemory errors. But don't it until the bitmap is not needed any more, because (from the javadoc)
it will throw an exception if
getPixels() or setPixels() is
called, and will draw nothing. This
operation cannot be reversed, so it
should only be called if you are sure
there are no further uses for the
bitmap.
In your case, I assume you should:
use thumbs which are bitmap opened with BitmapFactory.Options.inSampleSize or MediaStore.Images.Thumbnails
Have only at most one full size bitmap in memory (maybe one preloaded in you can afford)