Lazy Loading Images in a ListView

by Samuh » Tue, 08 Dec 2009 22:34:13 GMT


Sponsored Links
  trying a hand at ListViews and my current experiment is aimed at
displaying some data in a ListView. The data to be displayed in each
row is simple: an image and some text. The images come from a remote
server and the textual data is hardcoded.

I have a class that downloads images using AsyncTask and caches the
list of images fetched as SoftReferences in a LinkedHashMap. I am also
passing a reference of the view to this class, so when the image
download/cache read is complete the class will set appropriate Bitmap
in the view.

Following is my code:

//class BasicAdapter extends ArrayAdapter<RowData>
...
static class ViewHolder{
TextView text;
ImageView icon;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if(convertView == null){
convertView =
mInflater.inflate(R.layout.listview_row,null);

holder = new ViewHolder();
holder.text = (TextView)
convertView.findViewById(R.id.row_txt);
holder.icon = (ImageView)
convertView.findViewById(R.id.row_img);

convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}

holder.text.setText(mDataSet.get(position).getMText());

//set default icon
holder.icon.setImageResource(R.drawable.icon);

// set the actual image from the cache
String imageUrl = mDataSet.get(position).getMUrl();
WriteThroughCache.getImage(holder.icon, imageUrl);

return convertView;

}

// My helper class that manages cache and image Download

public class WriteThroughCache{
static LinkedHashMap<String,SoftReference<Bitmap>> imageCache =
new LinkedHashMap<String,SoftReference<Bitmap>>();

public static void getImage(ImageView icon, String imageUrl){
if(imageCache.containsKey(imageUrl)){
Bitmap image = imageCache.get(imageUrl).get();
if(image != null){
icon.setImageBitmap(image);
return;
}
}

new LoadImageInBackground().execute(new
Object[]{icon,imageUrl});

}

static class LoadImageInBackground extends AsyncTask<Object, Void,
Bitmap>{
ImageView mIcon = null;
String mUrl = null;
@Override
protected Bitmap doInBackground(Object... params) {
mIcon = (ImageView)params[0];
mUrl = (String) params[1];
Bitmap image = BitmapFactory.decodeStream
(GetMethodExecutor.getResponseStream(mUrl));
return image;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
mIcon.setImageBitmap(result);
imageCache.put(mUrl, new SoftReference<Bitmap>(result));
}


}
}

Problem: The applicatio



Lazy Loading Images in a ListView

by skyhigh » Wed, 09 Dec 2009 15:46:01 GMT


 I haven't tried to do a lazy background image load like you are doing,
but have done some code that uses a holder/wrapper to store the layout
findViewById which is a nice optimization so that it doesn't have to
call inflate and findViewById again when each row is displayed.  My
understanding is that when you do this it will allocate enough views
for each of the rows that are visible on the screen, and then as a row
scrolls off the screen, it will reuse the already initialized view for
a new row that is scrolling onto the screen.

In looking at your code I think you may have a problem because of the
way that the views for each row get reused when they disappear from
sight.  The same view that was used for a row that just disappeared
can be reused for a new row that is appearing as the list scrolls.
Your getView routine is handling this by detecting that the
convertView is non-null when it has already been filled in when this
view was used by a previous row.  However you don't have any mechanism
to detect when a view has been reused and is now displaying a
different row and the URL has changed to a different URL while the
background AsyncTask was retrieving the image.  By the time the
AsyncTask finishes retrieving the image, the view that it saved the
reference to, in order to set the image, could have been reused to
display a different row in the list.  If you can add code to check if
it is still the expected URL prior to setting the image I think it
might solve your problem.  If the URL has changed then skip setting
the lazy image on that view, and just add the image to your cache.

--


Sponsored Links


Lazy Loading Images in a ListView

by Romain Guy » Wed, 09 Dec 2009 16:08:00 GMT


 You can check out the example I wrote at code.google.com/p/shelves. It
does lazy loading of images from the sdcard.



>



Lazy Loading Images in a ListView

by Samuh » Thu, 10 Dec 2009 22:20:01 GMT


 skyhigh: thanks for your reply!


As you can see, I have used a view holder pattern too; as was
explained in the Google I/O conference by Romain.


Thanks for pointing this out; I was able to recreate the situation
that you've described by inserting a delay in the doInBackground()
method of AsyncTask. Result: When I scrolled a few dozen rows and
stopped, the icons in the new Frame displayed the older icons first
and then the list was refreshed.

I could probably work around this by:
1. Tag the view with some unique Id and when setting the image in the
AsyncTask, find the View by Tag, and then proceed with setImage(..).
2. I think, I could optimize the implementation further by not loading
the image when the list is scrolling; similar to what SlowAdapter does.

--



Lazy Loading Images in a ListView

by Samuh » Thu, 10 Dec 2009 22:21:28 GMT


 


Thanks Romain for your time; I will surely check the implementation
out.

Cheers!

--



Lazy Loading Images in a ListView

by Lee » Fri, 11 Dec 2009 02:48:03 GMT


 I have also recently been implementing adapter-based image loading
into a list.

I found that when I used SoftReferences, they started getting cleared
very quickly indeed, after my cache of thumbnails was up to maybe 20
or so.

When I use normal references, the cache grows happily to a couple of
hundred thumbnails without OOMs being thrown.

Being a java newbie, I don't pretend to understand this, but I went
with normal references and careful OOM catching.

BTW, tip maybe, I used onScroll and onScrollStateChanged from the list
to initiate cache fills (rather than filling as a result of calls from
getView()) because otherwise I found scrolling getting sluggish as the
image cache got busy
filling the cache while scrolling was still ongoing.

Lee






--



Lazy Loading Images in a ListView

by Lee » Fri, 11 Dec 2009 02:49:12 GMT


 Oh nevermind, I saw you realised the scroll problem already :-)

Lee

--



Lazy Loading Images in a ListView

by Samuh » Fri, 11 Dec 2009 11:40:34 GMT


 > I found that when I used SoftReferences, they started getting cleared

One of the possible reasons, I can think of, why Romain chose to
persist the fetched images on SDcard(in his Shelves app) is because
when caching is done on heap, the objects keep getting garbage
collected so you have to repeat the time consuming fetches.

--



Lazy Loading Images in a ListView

by Sam Dutton » Fri, 15 Jan 2010 06:23:51 GMT


  haven't read through the whole thread, but FWIW there's a very good
introduction to list view 'lazy loading' techniques in Beginning
Android: http://www.apress.com/book/view/1430224193.

Look in the sample code under fancylists -- well documented in the
book.

Sam Dutton

On Dec 8 2009, 2:34pm, Samuh <samuh.va...@gmail.com> wrote:
> Thanks.



Lazy Loading Images in a ListView

by Mark Murphy » Fri, 15 Jan 2010 06:38:11 GMT


 


Hmmmmm...

I wrote that book, and I'm pretty sure I didn't cover the OP's question
there.

It's related to the Fancy ListViews chapter, but adding asynchronous
thumbnail loading is a whole 'nuther kettle of fish.

Fortunately for the OP, I have already filleted, breaded, and seasoned
that kettle of fish. Here's a reusable component that offers
asynchronous thumbnail downloading and application to a ListView:

 http://github.com/commonsguy/cwac-thumbnail 

-- 
Mark Murphy (a Commons Guy)
 http://commonsware.com  |  http://twitter.com/commonsguy 

_The Busy Coder's Guide to *Advanced* Android Development_
Version 1.3 Available!



Other Threads

1. Updating Views in a ListView when they may have been recycled

Hi,

I've recently got into Android development and am writing a basic
podcast app as a learning exercise. I've come up with an issue I can't
find a good solution for. Rather than post code (it's getting big!),
I'll try and summarise:

I have an ExpandableListView showing podcasts and each podcast's
episodes in a simple two-level tree. The podcasts are downloaded in a
Service, and this Service 'sends' messages by using an interface
registered by the main Activity. This interface then uses a handler to
update the UI.

The child views in the ExpandableListView are LinearLayouts with one
TextView and two ViewStubs, one for showing an image and one for
showing a horizontal ProgressBar. These layouts are created if needed
in my extended BaseExpandableListAdapter's getChildView, or reused by
using the convertView supplied.

This all works fine and I'm happy with the overall model, except that
I can't figure out how to update a download's ProgressBar efficiently
when the Activity receives a callback from the Service. How do I get a
reference to the correct View to set its progress?

I've tried keeping a cached HashMap to track which episode currently
has which View instance, but this didn't work due to me recycling the
Views via convertView - I found that when more than one download is
active the two bars can swap values back and forth.

Any help would be appreciated.

Thanks,

Neil

--~--~---------~--~----~------------~-------~--~----~

2. How to get the Count for the Outgoing SMS in between the particular date

Hi,

in Android Mobile i need to get the SMS count for the outgoing message
between the particular date /  or taken the count from the given date
to the current Date.


as the same like the call counts also out going call counts

Please Help me to found that  email me to my Id :
damu...@gmail.com


Thank you


--~--~---------~--~----~------------~-------~--~----~

3. develop picasa in android

4. French Version Of Datepicker

5. WifiReceiver not part of SDK 1.5???

6. how to package several android projects

7. R.arawable visuals