How to lazy load images in ListView in Android

ID : 435

viewed : 199

Tags : androidimagelistviewurluniversal-image-loaderandroid

Top 5 Answer for How to lazy load images in ListView in Android

vote vote

91

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() {             @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();     } } 
vote vote

82

I made a simple demo of a lazy list (located at GitHub) with images.

Basic Usage

ImageLoader imageLoader=new ImageLoader(context); ... imageLoader.DisplayImage(url, imageView);  

Don't forget to add the following permissions to your AndroidManifest.xml:

 <uses-permission android:name="android.permission.INTERNET"/>  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please 

create only one instance of ImageLoader and reuse it all around your application. This way image caching will be much more efficient.

It may be helpful to somebody. It downloads images in the background thread. Images are being cached on an SD card and in memory. The cache implementation is very simple and is just enough for the demo. I decode images with inSampleSize to reduce memory consumption. I also try to handle recycled views correctly.

Alt text

vote vote

70

I recommend open source instrument Universal Image Loader. It is originally based on Fedor Vlasov's project LazyList and has been vastly improved since then.

  • Multithread image loading
  • Possibility of wide tuning ImageLoader's configuration (thread executors, downloader, decoder, memory and disc cache, display image options, and others)
  • Possibility of image caching in memory and/or on the device's file system (or SD card)
  • Possibility to "listen" loading process
  • Possibility to customize every display image call with separated options
  • Widget support
  • Android 2.0+ support

vote vote

67

Multithreading For Performance, a tutorial by Gilles Debunne.

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

  • AsyncTasks.
  • A hard, limited size, FIFO cache.
  • A soft, easily garbage collect-ed cache.
  • A placeholder Drawable while you download.

enter image description here

vote vote

57

Update: Note that this answer is pretty ineffective now. The Garbage Collector acts aggressively on SoftReference and WeakReference, so this code is NOT suitable for new apps. (Instead, try libraries like Universal Image Loader suggested in other answers.)

Thanks to James for the code, and Bao-Long for the suggestion of using SoftReference. I implemented the SoftReference changes on James' code. Unfortunately, SoftReferences caused my images to be garbage collected too quickly. In my case, it was fine without the SoftReference stuff, because my list size is limited and my images are small.

There's a discussion from a year ago regarding the SoftReferences on google groups: link to thread. As a solution to the too-early garbage collection, they suggest the possibility of manually setting the VM heap size using dalvik.system.VMRuntime.setMinimumHeapSize(), which is not very attractive to me.

public DrawableManager() {     drawableMap = new HashMap<String, SoftReference<Drawable>>(); }  public Drawable fetchDrawable(String urlString) {     SoftReference<Drawable> drawableRef = drawableMap.get(urlString);     if (drawableRef != null) {         Drawable drawable = drawableRef.get();         if (drawable != null)             return drawable;         // Reference has expired so remove the key from drawableMap         drawableMap.remove(urlString);     }      if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);     try {         InputStream is = fetch(urlString);         Drawable drawable = Drawable.createFromStream(is, "src");         drawableRef = new SoftReference<Drawable>(drawable);         drawableMap.put(urlString, drawableRef);         if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "                 + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "                 + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());         return drawableRef.get();     } catch (MalformedURLException e) {         if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);         return null;     } catch (IOException e) {         if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);         return null;     } }  public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {     SoftReference<Drawable> drawableRef = drawableMap.get(urlString);     if (drawableRef != null) {         Drawable drawable = drawableRef.get();         if (drawable != null) {             imageView.setImageDrawable(drawableRef.get());             return;         }         // Reference has expired so remove the key from drawableMap         drawableMap.remove(urlString);     }      final Handler handler = new Handler() {         @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(); } 

Top 3 video Explaining How to lazy load images in ListView in Android

Related QUESTION?