Android: lazy loading in Gallery - android

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.

Related

Reusing Views when they are already assigned to the same element

In my app I'm displaying images on a grid view. In some cases there are a a lot of images on a single grid view (up to 1,000). This is driven off user data and is unavoidable.
It's impractical to load all images in memory. I tried that and was hitting memory errors fairly quickly. So to avoid this I create AsyncTasks in the getView() method of my array adaptor which load the image in the background. This works very smoothly so far.
The problem is that the set of images can change. notifyDataSetChanged() will trigger getView() to be called on every item on screen. The result is a lot of CPU time spent re-loading images which don't need to be changed.
Is there any way to detect if getView() is "reusing" a view to display the same element as it's already displaying? Alternatively is it possible to detect if an ImageView has already has a Bitmap assigned?

android dynamically show list with text and images, improving performance

I need to display a list of items each with text and various number of images.
I'm currently using a ListView with custom adapter to show these items. And for each item, I used a HorizontalScrollView with a LinearLayout in it to display the images. In the getView method of the ListView, I read the image URIs of each item and dynamically create ImageViews, then load the images asynchronously. I used a ViewHolder to hold the LinearLayout which contains all the ImageViews of each item.
The problem is, if I scroll down the ListView and scroll back, I'll lost the content of the item, which means I have to load the images again. And most of the images are too large and loads very slow. Actually on my app the screen can show only about 2 or 3 items once, so the scroll happens very frequently.
I have some ideas to improve this, but I'm not sure whether one of them will results better.
Since I'm just showing a thumbnail of each image, maybe I can save the thumbnails into a temp dir and load them dynamically, loading small images will be much faster. And I might have to clear that temp dir when it gets large.
I have at most 9 images for each item, so it might still be slow even if I cache the thumbnails and scroll frequently. And maybe I have to show the list manually instead of using ListView, so each item will not be reused, and the load will happen only once. But, the list will grow large in the future, if I preserve like 100 items in the LinearLayout my app may still crash.
Other better options...
Any advice will be helpful! Thanks!
You can use LazyLoading to display your image. You can use Universal Image Loader for this purpose. What lazyloading does is download an image once, cache it and display the image from cache during subsequent requests.

How to load images that aren't on the screen

I currently have a gridview that displays some thumbnail images. The problem I am running into is that when I scroll through the images I can see the images switch from old to new as they are being replaced. So, I was wondering how I would fix this. Is there a way to load images that aren't on the screen so when I scroll the user doesn't have to see the change in images?
The best way to avoid that effect is to set the image first to null in the getView() method of your adapter. You can then check an LRU cache if the image is already loaded. If yes, then set it right away, if not then load it asynchronous as a bitmap. Set it to the view and add it to the LRU cache.

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: Turn off lazy loading of listview

In my Android App I have a listview containing 30 rows, and each row consists of several textviews of which one is spannable and sometimes contains a lot of formatted text and images.
Those images are loaded from the web asynchroneously: A placeholder is displayed until the image has been downloaded is then replaced by the image.
Unfortunately, the rows of the listview are loaded when I scroll over them. This makes the whole thing very slow. Also, those images are loaded from the web again and again, whenever I scroll over the row.
How can I turn it off, that the ListView rows are loaded when I scroll over them? They should be loaded once when I start the activity and never again.
Best regards and thanks in advance,
Jan Oliver
When you do a lazy-loading ListView, is because you want to speed it up your app. Turn it off is not the best solution. So, what you can do is implementing a basic cache system in order to avoid downloading and setting the ImageView again and again.
The easiest way to do so is implementing a HashMap with URLs as keys and Bitmaps as values. Something like this:
Map cache = new HashMap();
// then, on your lazy loader
Bitmap image = cache.get(urlOfTheImage);
if( image == null ){
// download and decode the image as normal,
// then assign the decoded bitmap to
// the 'image' variable
cache.put(image);
}
imageView.setImageBitmap(image);
If those images will be the same always, meaning that each time you open the app the same images will be downloaded, then you better save those images in the filesystem and use them from there.
On the other hand, if the images tend to change, you could implement some interesting stuff: use SoftReferences. There's an explanation in this video. This can also be used if you are loading images from the filesystem.
Edit
With regards to your comment, I highly recommend you watching the video I posted. It's one hour long, but it really worths the effort. When using an adapter, checking if the convertView is null is just a simple way to improve performance, though there are some other techniques that will improve your app even more. Also, if you had have problems while using that trick, is because you are probably implementing it the wrong way. Remember: even if you don't re-inflate the views, you do have to set the value of each one of the children views, otherwise you will experience some problems.
If you can, start with an Image Array full of the "placeholder images", then download the images in to an Array firing an AsyncTask during on Create. During row view building just refer to the array. That way if it has the new image it will load it, if not it will get the placeholder.
If you have a lot of data its gonna get real slow and be a crappy expirience for the user.
Create a list of objects that represent each row. Create a loader as a background thread that updates the objects as it loads the data. Your list view will draw data from the objects.
(Not a good idea if you have hundreds of rows and a huge amount of data in each row - in that case, you should only load data within a few rows of the currently active row and have some sort of MRU cache).

Categories

Resources