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.
Related
For last 10+ hours I try to get a large (40+) amount of images (in ImageButton format) on a single Android screen without out of memory errors. The activity I work on is an image picker for a coloring book app. Images are of various sizes in the range of (500 to 1200)x(500 to 1200), PNGs (if that matters).
I have tried:
Horizontal Scroll View with images added from the code. The result is slow (I do it on UI thread) and consumes large memory space.
Horizontal Scroll View with images added from the code via AsyncThread. The result is fast but still consumes large memory space. I like the user experience of this one the most!
Grid View and List View - both are very choppy (testing on first Nexus 7). Memory usage is better.
What I am considering
View Pager - first results look better than Grid View from performance perspective (I have not completed it to the moment to assess the memory usage but it should be better from what I understand). Yet, I dislike the user experience and prefer a scrollable list of all images.
Conversion of my resources to JPG (will that get rid of Transparency byte?)
Downsizing the images to max 500x500px
None of the solutions seems as good as the Android Photo Gallery app available on all devices. This experience is what I would love to mirror. No idea how this is done though :(
Two questions.
What is the best way to get such thing (40+ Images scrollable on single screen) done? Is it really ViewPager? ScrollView with AsyncTask and well thought images resolution? Something I have not tried yet?
What is the memory limit I should try to keep below? Looking at Android heap size on different phones/devices and OS versions it seems to be 256 MB, is that fair assumption?
Thanks. Have a great weekend!
PS. On iOS all works like charm when I add all the buttons into a scroll view :(
Some basic calculations reveals your problem:
40+ images at 1200x1200 = approx 57MB, the comments are correct you need to subsample (i.e. thumbnail) the images or use a library such as the Universal Image Loader. Converting to JPG doesn't matter. That is a compressed storage format, the memory required to display the pixels remains the same.
There is a Android Developers article on Loading Large Bitmaps Efficiently with sample code. The following steps are covered in more detail in the article Android Bitmap Loading for Efficient Memory Usage.
The basic steps in loading a large bitmap are:
Determine the required size (from a known size or determining the
size of the target View).
Use the BitmapFactory class to get the bitmap’s size (set inJustDecodeBounds in BitmapFactory.Options to true).
Calculate the subsampling value and pass it to the BitmapFactory.Options setting inSampleSize.
Use the BitmapFactory to load a reduced size bitmap.
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).
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);
What is the most efficient way to display a grid of random images on android?
I have a list of random album art images, I need to generate a grid out of them and use it as a background in my activity, the images are downloaded asynchronously, scaled down and cached, displaying them in a grid seems to consume a lot of RAM,(yes I'm recycling the bitmaps, and using LRU cache)
Would drawing them to a canvas be a better solution? Are there other efficient ways to do that?
Is the GridView safe, are there any guarantees that it won't run out of memory?
P.S. drawing them to a canvas would require me to redraw when the orientation or activity's size changes.
I don´t think there is a more efficient way, at least I can not think of one right now. What you could do, that depends if grid view is necessary, is using the Image carousel from Romain Guy which is made in Renderscript. I do not have the URL at the moment but it is a google-code project.
Another page where sometime are good stuff for those things is http://www.theultimateandroidlibrary.com/all
The LRUCache is really nice and fast and should do the trick. I also would be interested in a more efficient way.....
I'm creating a simple Gallery of drawables - each of them is almost the size of a screen, so they require quite much memory. For each entry I'm creating a custom LinearLayout with ImageView and TextView for the title. As most of you know, android Gallery doesn't recycle views so it gallery will crash easily on low-memory phones (after loading 4 drawables on 16mb ram limit, in my case).
Here's the simple question - how do you implement such gallery, so it won't run out of the memory? How do you recycle these images? A working code example would be great.
Few notes:
inSampleSize isn't a way to go, I can't scale these images down
Calling recycle() on Drawable's loaded from resource is impossible, as it will crash on Android 4.0+ (it will recycle the drawable in their internal cache)
Don't ask me to post the code, as there is no.
You shouldn't be using Gallery because it's deprecated. Especially since there isn't any code written so far. The documentation suggests using a HorizontalScrollView or ViewPager.
I feel a ViewPager is what your looking for because it will only keep at most 3 pictures in memory and handels all the recycling for you. Here is a post with more information about how to implement one android viewPager implementation