Tips to speed up Gallery widget in Android Honeycomb - android

I'm looking for a great way to speed up the Gallery view widget in Android Honeycomb. I'm currently using it to display some fairly large images at roughly 340 x 600 pixels, and I'd love for it to be smooth as butter when scrolling through the images.
It's fairly speedy at the moment, but it doesn't compare to loading a ScrollView with ImageViews and scrolling through that.
Here's a simplified version of my getView() method from my custom BaseAdapter:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = (ImageView) new ImageView(Main.this);
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
((ImageView) convertView).setImageBitmap(createReflection(BitmapFactory.decodeFile(ImageFile, options)));
convertView.setPadding(20, 0, 20, 0);
return convertView;
}
I've been experimenting with lazy loading the images, but I didn't really like the result.

The difference between using a Gallery and:
loading a ScrollView with ImageViews and scrolling through that
is that with the ScrollView scenario, you are pre-loading all of the images, rather than loading them on the fly as you are in the Gallery scenario.
If your number of images is small, and you have enough RAM to support all of them, then just use your ScrollView.
Beyond that, AFAIK there's not a ton you can do. You can maintain a bitmap cache where you continue decoding a few images ahead of the current ones in the Gallery and have your Adapter pull from the cache. However, that will only get you so far -- small scrolls will be smooth, but flings past your cache capacity will still result in the decoding being done on demand. That's pretty much unavoidable.

Gallery does not support the convertView at the moment. You will always receive null for convertView. This is a known issue and there's no ETA for a fix.

Turning on Hardware acceleration of your application will have a significant impact (at least had it on my example app).
Add android:hardwareAccelerated="true" in the application element of your android manifest
http://developer.android.com/guide/topics/manifest/application-element.html#hwaccel

One of the elements which is slowing your Gallery view down is file IO.
I'm working on a slideshow app for which I have photos in 1280x720 resolution.
The actual file I/O takes 300-400 ms for each file.
Since the File I/O would normally run on the UI thread, this will cause a very visible "hick" in any ongoing photo transition.
In order to avoid this, you should:
Set a temporary loading drawable that is already cached
imageView.setImageResource(R.drawable.my_loading_drawable);
Create an AsyncTask which
loads the drawable in doInBackground(i.e. the File I/O)
Updates the imageView in onPostExecute
imageView.setImageDrawable(drawable);
PS if the user flicks through multiple pictures, the above approach will typically trigger multiple parallell asynctask which all will be using file I/O. Not good for performance and may crash your app. You should probably have more structured approach where you only allow one async task at a time.

Related

Images not loaded into ListView when scrolling fast

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).

Listview items remove bitmaps from memory when the user scrolls

I am working on an android application. I have an activity that contains a list view.
The list view has lots of items and each contains a bitmap image.
Because the list is very big and contains a lot of images it is giving and out of memory error on some old devices when loading the bitmaps.
So I am looking for a way to do the following for old devices:
Load the bitmaps for the items that are in the view (or near it)
When the user scrolls down I load the bitmaps that should appear to him and remove the bitmaps that are now hidden.
So at anytime only the images that are in the view (or near it) are loaded in the Heap while the others are removed when they are no longer in the view.
Is there a way to do it?
Additional information:
I have followed every instructions and code on how to load Bitmap images efficiently (on the android documentation and on any site that I could find), I also made sure there is no memory leaks, I recycle every bitmap and make sure the GC works correctly and finally, on old devices I made the resolution of the images the smallest possible. But the size of the list is causing the out of memory to still appear on some devices.
So I am looking to know if I can do the above proposed solution.
Thanks a lot for any help
You should consider looking at Sample Gallery App from Google I/O 2012. Everything you need is implemented there.
Video from Google I/O about gallery app
Source code is available on Google Code and it's an exclellent example of building a Gallery. You should also consider looking at Romain Guy's presentation (beginning) where he explains performance issues with ListView and teaches to use adapters efficiently. (ViewHolder pattern and reusing views).
Here's a RecycleListener for GridView (Available from API level 1). A RecyclerListener is used to receive a notification whenever a View is placed inside the RecycleBin's scrap heap. This listener is used to free resources associated to Views placed in the RecycleBin.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.album);
mAdapter = new PhotoAdapter(this);
mGridView = (GridView) findViewById(android.R.id.list);
mGridView.setAdapter(mAdapter);
mGridView.setRecyclerListener(new RecyclerListener() {
#Override
public void onMovedToScrapHeap(View view) {
// Release strong reference when a view is recycled
final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
imageView.setImageBitmap(null);
}
});
// Kick off loader for Cursor with list of photos
getLoaderManager().initLoader(LOADER_CURSOR, null, mCursorCallbacks);
}
You wanna search some tutorials and examples for 'lazy loading'.
One possible example would be: https://github.com/thest1/LazyList
Using a library like Universal Image Loader gives you options for caching images and memory management. You should be able to set a maximum cache size through that and it also takes care of loading images on a background thread.
The ListView takes care of recycling the views that you put the images in and going easier on memory in that respect.

Android ArrayAdapter increasing cached views to improve performance

So I have a listview with an image and some text hooked by a custom array adapter. The problem I am facing is that the image is still loading a little bit too slow for my liking. I've watched the google tech talk and attempted to optimize my list by resusing the view by the convertView (if it's null then inflate, if not, resuse). I've also spawned AsyncTasks to load the bitmap with BitmapFactory.Options inSampleSize set to a power of 2, since the image is relatively small. I've also used the ViewHolder pattern to reduce findViewById calls.
However, even with this, when I scroll through my list, it is very noticeable how the convertViews are being reused because the image constantly gets updated as I scroll up and down.
What I noticed in the stock Android photo gallery is, with the hundreds of photos that I have, when I scroll through quickly, the photos initially load slowly, but then get cached. The amount cached is MUCH more than what the screen is capable of showing, so as I scroll, initially the photos load seamlessly until I scroll pass the amount of photos cached, and then the reload of photos is noticeable again.
Is there a way to do this with the ArrayAdapter? Basically, store more than the 9 views within my listview (What my screen is capable of showing) for the purposes of when a user scrolls down quickly, the user would have to scroll down a lot before the convertView is reused and thus the noticeable image reloads?
Thanks in advanced!
Load the images in the background, using multiple parallel threads.
For example: Sending Operations to Multiple Threads; the sample app downloads thumbnails from the Picasa Featured Photos RSS feed. Hope this helps.

GridView with many images results in slow/rough scrolling

In my app I have a GridView with many images (~100), layout similar to this one:
I'm testing my app on a HTC Desire HD. When I have 88 images in the gridview, the scrolling is smooth. Now, if I add 10 images, so I have 98 in total, there isn't anything close to smooth scrolling anymore - it's now "slow", more rough, it's like it's scrolling in big chunks.
This gridview is present in a TabLayout, in which I also have a ListView with many elements. When using 98 images in the gridview, also scrolling in this listview is affected. So it's like the whole application gets slower somehow (or uses more "power" than the phone can manage).
Is there any way I can optimize the gridview so it does not use so much power?
(PS: when using about 150 images, the application simply stops working (it disconnects, and no error is shown in the log/debug).)
Make sure you are recycling your images in the getView() method of the adapter. Something like this:
public View getView(int position, View convertView, ViewGroup parent) {
// if it's not recycled, initialize some attributes
if (convertView == null) {
convertView = new View(...);
}
...
instead of making new Views each time getView() is called.
In the end I went with the solution provided by NOSTRA here:
Lazy load of images in ListView
It works really well.
I don't code in Android but I'd suggest a lazy visibility approach.
All your images appear to be the same size, so handle the scroll event and show only the images that can be seen on the current view frustum (the screen) while hiding the the rest.
My guess is, Android is using its processing power on the images that can't be seen during scrolling which causes the jitters.
Firstly, do not keep all your bitmaps in memory private Bitmap[] mBitmap;. That's probably what crashes your app. Read them from the filesystem with BitmapFactory.
Secondly, you should not scale your images in your adapter, that is expensive:
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
Try with a set of images that do not need to be scaled.

Android: lazy loading in Gallery

I've reviewed some posts about lazy loading but I believe my problem is a bit different.
I have a gallery (my class extends Gallery) which displays 20 rather large in size images (400-500K each). I cannot load them all to gallery since I get an OutOfMemory exception.
So, I created an array of 20 Drawables and initially populated the first 9 elements (the images come from the Web) and set all the rest to null. My intention was this: on a fling to the right, fetch element no. 10 and set to null element no. 0. On another fling to the right fetch element no. 11 and set to null element no. 1 to null. Same logic on a fling left.
The problem is I can fling much faster than the elements are fetched.
My gallery has a BaseAdapter and its getView() looks something like this:
public View getView(int position, View convertView, ViewGroup parent){
ImageView imageView = new ImageView();
imageView.setDrawable(imageArray[position];
....
....
return imageView;
}
How do I tell getView() - if imageArray[position] is still null, show a "loading..." dialog and once it is set repeat yourself with the same position?
I don't want to see the imageView empty and then set on the fly. I want to not be able to see the imageView at all until it is set.
Thanks.
Gallery is designed for smooth experience. It will be very bad UI if you block the screen and don't switch to next image until it is fetched. This way user will not be able to fling at all. You should display some loading indicator instead of image while it is loading.
I think your scenario is rather common. You should download images and display them. If you get OutOfMemory you can try to supersample images Strange out of memory issue while loading an image to a Bitmap object.
If there's still OutOfMemory you should remove bitmaps from memory and cache them to SD. So when user flings back you can load images from SD again, it will be fast enough. And memory consumption will be lower. As you suggest you can have 10 most recent images cached in memory and others cached on SD.
You can take a look at my sample code Lazy load of images in ListView. Actually it's a ListView adapter but you can apply it to gallery with minor modifications. I think it will do exactly what you need.

Categories

Resources