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. Largish HTTP Post vs. ANR

Hi all,

I'm using a helper thread to post data back to a web site. What I
notice is that I'm
getting an Application Not Responding back in the main thread even
though it's
not doing anything other than interrupting the helper thread to wake
up. If the
amount of data in the post is small (~ couple hundred bytes), I don't
have a
problem, but when it gets moderately large (~ 10kb) it reliably stalls
the main
UI thread and gets (appropriately) an ANR. Even weirder is that if I
time from
when I start the post to when it receives the entire reply (ie, using
Date().getTime())
in the helper thread, it shows that it's only taking about .5 seconds
to complete,
even though it  takes ~10 seconds to become responsive again.

This *only* seems to be the case writing the post data. If I do a post
of a small
amount of data with a large amount of data coming back from a web
site, the
app remains responsive and unlike the above problem, the timing
numbers in
the thread agree with how long it actually took.

FWIW, I'm using HttpURLConnection to create and read/write the data,
and
I'm aware that I need to use Handler to affect the UI from the helper
thread.
Also: this is on the G1 hardware. I've also fiddled with priorities,
etc, to
no avail, so I'm pretty stumped as to why the helper thread is
interacting
with the UI thread.

Mike

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

2. Problems with GPS location service on r30

Hi here,
   First of all, I'd like to say how amazed I am with the android API.
It's been awesome to work with for my first project. I'm planning on
doing lots more with it in the future.
I made my own jogging tracker and it was easy and fast.
However, Once I got the data over to my computer and formatted
properly, it looks really funky:
http://www.gpsvisualizer.com/display/1228462535-27640-208.127.241.176.html

I can give you the code if you want. I'm planning on posting it to a
android apps site.

To reproduce the above map, goto 
http://www.gpsvisualizer.com/map_input?form=google
and paste in the raw data at http://pastebin.ca/1276690

Other people have complained that the accurancy of the GPS location
provider dropped after the r30 update. I just wanted to let you know
too

Cheers!
Ben

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

3. TranslucentBlurActivity help

4. Using Raw MapTiles

5. Kogan Agora Pro skin now available for Android Emulator!

6. Newbe question - Application folders, how to?

7. Top Status Bar Webkit transparent