Lazy Load Images on Listview in Android(Beginner Level)

Lazy Load images on Listview in android(Beginner Level)?

Praveen -

As you already found my blog post on this, I just wanted to push it back to Stackoverflow so that others can use it.

Here's the basic discussion:
http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/

And there's a class I documented later that uses a thread and a callback to load the images:

http://ballardhack.wordpress.com/2010/04/10/loading-images-over-http-on-a-separate-thread-on-android/

Update: To address your specific exception, I think that view returned in the list from getChildAt is not an ImageView -- it's whatever layout view you are using to hold the image and text.

Update to include relevant code: (Per @george-stocker's recommendation)

Here is the adapter I was using:

public class MediaItemAdapter extends ArrayAdapter<MediaItem> {
private final static String TAG = "MediaItemAdapter";
private int resourceId = 0;
private LayoutInflater inflater;
private Context context;

private ImageThreadLoader imageLoader = new ImageThreadLoader();

public MediaItemAdapter(Context context, int resourceId, List<MediaItem> mediaItems) {
super(context, 0, mediaItems);
this.resourceId = resourceId;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.context = context;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

View view;
TextView textTitle;
TextView textTimer;
final ImageView image;

view = inflater.inflate(resourceId, parent, false);

try {
textTitle = (TextView)view.findViewById(R.id.text);
image = (ImageView)view.findViewById(R.id.icon);
} catch( ClassCastException e ) {
Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e);
throw e;
}

MediaItem item = getItem(position);
Bitmap cachedImage = null;
try {
cachedImage = imageLoader.loadImage(item.thumbnail, new ImageLoadedListener() {
public void imageLoaded(Bitmap imageBitmap) {
image.setImageBitmap(imageBitmap);
notifyDataSetChanged(); }
});
} catch (MalformedURLException e) {
Log.e(TAG, "Bad remote image URL: " + item.thumbnail, e);
}

textTitle.setText(item.name);

if( cachedImage != null ) {
image.setImageBitmap(cachedImage);
}

return view;
}
}

How to lazy load images in ListView in Android

Here's what I created to hold the images that my app is currently displaying. Please note that the "Log" object in use here is my custom wrapper around the final Log class inside Android.

package com.wilson.android.library;

/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
private final Map<String, Drawable> drawableMap;

public DrawableManager() {
drawableMap = new HashMap<String, Drawable>();
}

public Drawable fetchDrawable(String urlString) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}

Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");

if (drawable != null) {
drawableMap.put(urlString, drawable);
Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
} else {
Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
}

return drawable;
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
if (drawableMap.containsKey(urlString)) {
imageView.setImageDrawable(drawableMap.get(urlString));
}

final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};

Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}

private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}

How to lazy load images in ListView in Android

Here's what I created to hold the images that my app is currently displaying. Please note that the "Log" object in use here is my custom wrapper around the final Log class inside Android.

package com.wilson.android.library;

/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
private final Map<String, Drawable> drawableMap;

public DrawableManager() {
drawableMap = new HashMap<String, Drawable>();
}

public Drawable fetchDrawable(String urlString) {
if (drawableMap.containsKey(urlString)) {
return drawableMap.get(urlString);
}

Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
try {
InputStream is = fetch(urlString);
Drawable drawable = Drawable.createFromStream(is, "src");

if (drawable != null) {
drawableMap.put(urlString, drawable);
Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
+ drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
+ drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
} else {
Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
}

return drawable;
} catch (MalformedURLException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
} catch (IOException e) {
Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
return null;
}
}

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
if (drawableMap.containsKey(urlString)) {
imageView.setImageDrawable(drawableMap.get(urlString));
}

final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message message) {
imageView.setImageDrawable((Drawable) message.obj);
}
};

Thread thread = new Thread() {
@Override
public void run() {
//TODO : set imageView to a "pending" image
Drawable drawable = fetchDrawable(urlString);
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
};
thread.start();
}

private InputStream fetch(String urlString) throws MalformedURLException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet request = new HttpGet(urlString);
HttpResponse response = httpClient.execute(request);
return response.getEntity().getContent();
}
}

how to use lazy load or async task in List View

So I usually don't offer advice if there isn't any clear attempt/code to show for, but I remember when I was first confronted with AsyncTasks and threading in general and how it was a bit confusing at first, so I'll get you started on the right track.

So an AsyncTask basically runs a process that may take a while (such as loading information from a server or, in your case, fetching files). It has a couple methods that are detailed here, but I believe, for your scenario, you will simply need to use the doInBackground and onPostExecute methods. What you'll probably be doing in each of those is getting the actual images and data for your ListView in doInBackground and then updating the ListView to display that data in onPostExecute. Consider the examples outlined here. Essentially, the doInBackground method will send some data (in this case your files and other stuff - usually in a form of an array or List or something if there's lots of data being sent) to the onPostExecute method which will handle the data from there.

What's happening is that you're overburdening your main UI thread and the program will wait for the fetching and loading of the images intermittently with your UI. AsyncTask takes care of this for you by throwing all the work to separate worker threads. Problems usually arise when overburdening the UI thread, such as your program closing unexpectedly because Android notices that the main application thread is using too many resources (no actual bugs in your code).

As for the Lazy Load of the image data, I'm not completely sure how it works (never had to use it), but this seems really helpful.

Hope that gave you some direction.

Lazy loading images only works if I don't preset ImageView image?

a hunch:

remove the notifyDataSetChanged().

Why? the call adapter.notifyDataSetChanged() will provoqc a FULL refresh of the list (ie: of each view). And so, a new call for each item to getView(position). In this call you change again the image to default_icon. This append JUST AFTER setting the good one!

so the sequence is: set default_icon for one image, load one image from disk, invalidate -> set default for ALL, load one image from disk, ....

EDIT: clarify explanation, remove assumption about thread limit.

Android ListView with lots of same drawables performance

Try to use lazy loading technique using caching, you can find many tutorial on the web:

You have to see this SO thread

Also see this:

Multithreading For Performance, a tutorial by Gilles Debunne.

This is from the Android Developers Blog. The suggested code uses:

  1. AsyncTasks.
  2. A hard, limited size, FIFO cache.
  3. A soft, easily garbage collected cache.
  4. A placeholder Drawable while you download.


Related Topics



Leave a reply



Submit