SoFunction
Updated on 2025-04-10

Detailed explanation of the asynchronous loading of Android ListView image method

This article describes the asynchronous loading of images by Android ListView. Share it for your reference, as follows:

Let’s talk about the advantages of this article first. Turn on the thread to load pictures asynchronously, then refresh the UI to display pictures, and cache the pictures loaded by the network through weak references, saving the overhead of connecting to the network again.

This is undoubtedly a very desirable method, but you will still feel a slight screen stuck when loading the picture, especially when the item in the listview slides quickly.

I looked for the reason. Maybe it was that there were too many items passing by when the listview quickly swiped the screen. And every time I called the getView method, I would use the handler to refresh the UI asynchronously in a certain time in the past.

If handler is called at the same time, it will cause such screen jamming.

Later, I thought that there is no need for us to load images in the background when the listview is sliding (regardless of whether the image is in the cache or on the network), which undoubtedly causes a lot of waste of resources.

We just need to load the pictures in a few items displayed in the listview after the listview slide stops.

Based on the above ideas, I have done some design modifications:

1. Start the thread that loads the image in the getview method of the adapter. If the listview is sliding, wait

2. Listen to the listview sliding stop event, get the top and bottom serial numbers of the item displayed by the listview, and wake up all threads of loading pictures, determine whether the serial numbers of the loading pictures are within the range, if so, continue to load, and if not, end threads

Some of the codes are as follows:

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
  if(convertView == null){
    convertView = (.book_item_adapter, null);
  }
  BookModel model = (position);
  (position);
  ImageView iv = (ImageView) ();
  TextView sItemTitle = (TextView) ();
  TextView sItemInfo = (TextView) ();
  (model.book_name);
  (model.out_book_url);
  (.rc_item_bg);
  (position,model.out_book_pic,imageLoadListener);
  return convertView;
}
 imageLoadListener = new (){
  @Override
  public void onImageLoad(Integer t, Drawable drawable) {
    //BookModel model = (BookModel) getItem(t);
    View view = (t);
    if(view != null){
      ImageView iv = (ImageView) ();
      (drawable);
    }
  }
  @Override
  public void onError(Integer t) {
    BookModel model = (BookModel) getItem(t);
    View view = (model);
    if(view != null){
      ImageView iv = (ImageView) ();
      (.rc_item_bg);
    }
  }
};
public void loadImage(){
  int start = ();
  int end =();
  if(end >= getCount()){
    end = getCount() -1;
  }
  (start, end);
  ();
}
 onScrollListener = new () {
  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {
    switch (scrollState) {
      case .SCROLL_STATE_FLING:
        ("SCROLL_STATE_FLING");
        ();
        break;
      case .SCROLL_STATE_IDLE:
        ("SCROLL_STATE_IDLE");
        loadImage();
        //loadImage();
        break;
      case .SCROLL_STATE_TOUCH_SCROLL:
        ();
        break;
      default:
        break;
    }
  }
  @Override
  public void onScroll(AbsListView view, int firstVisibleItem,
      int visibleItemCount, int totalItemCount) {
    // TODO Auto-generated method stub
  }
};

package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
public class SyncImageLoader {
  private Object lock = new Object();
  private boolean mAllowLoad = true;
  private boolean firstLoad = true;
  private int mStartLoadLimit = 0;
  private int mStopLoadLimit = 0;
  final Handler handler = new Handler();
  private HashMap<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
  public interface OnImageLoadListener {
    public void onImageLoad(Integer t, Drawable drawable);
    public void onError(Integer t);
  }
  public void setLoadLimit(int startLoadLimit,int stopLoadLimit){
    if(startLoadLimit > stopLoadLimit){
      return;
    }
    mStartLoadLimit = startLoadLimit;
    mStopLoadLimit = stopLoadLimit;
  }
  public void restore(){
    mAllowLoad = true;
    firstLoad = true;
  }
  public void lock(){
    mAllowLoad = false;
    firstLoad = false;
  }
  public void unlock(){
    mAllowLoad = true;
    synchronized (lock) {
      ();
    }
  }
  public void loadImage(Integer t, String imageUrl,
      OnImageLoadListener listener) {
    final OnImageLoadListener mListener = listener;
    final String mImageUrl = imageUrl;
    final Integer mt = t;
    new Thread(new Runnable() {
      @Override
      public void run() {
        if(!mAllowLoad){
          ("prepare to load");
          synchronized (lock) {
            try {
              ();
            } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              ();
            }
          }
        }
        if(mAllowLoad && firstLoad){
          loadImage(mImageUrl, mt, mListener);
        }
        if(mAllowLoad && mt <= mStopLoadLimit && mt >= mStartLoadLimit){
          loadImage(mImageUrl, mt, mListener);
        }
      }
    }).start();
  }
  private void loadImage(final String mImageUrl,final Integer mt,final OnImageLoadListener mListener){
    if ((mImageUrl)) {
      SoftReference<Drawable> softReference = (mImageUrl);
      final Drawable d = ();
      if (d != null) {
        (new Runnable() {
          @Override
          public void run() {
            if(mAllowLoad){
              (mt, d);
            }
          }
        });
        return;
      }
    }
    try {
      final Drawable d = loadImageFromUrl(mImageUrl);
      if(d != null){
        (mImageUrl, new SoftReference<Drawable>(d));
      }
      (new Runnable() {
        @Override
        public void run() {
          if(mAllowLoad){
            (mt, d);
          }
        }
      });
    } catch (IOException e) {
      (new Runnable() {
        @Override
        public void run() {
          (mt);
        }
      });
      ();
    }
  }
  public static Drawable loadImageFromUrl(String url) throws IOException {
    (url);
    if(().equals(Environment.MEDIA_MOUNTED)){
      File f = new File(()+"/TestSyncListView/"+MD5.getMD5(url));
      if(()){
        FileInputStream fis = new FileInputStream(f);
        Drawable d = (fis, "src");
        return d;
      }
      URL m = new URL(url);
      InputStream i = (InputStream) ();
      DataInputStream in = new DataInputStream(i);
      FileOutputStream out = new FileOutputStream(f);
      byte[] buffer = new byte[1024];
      int  byteread=0;
      while ((byteread = (buffer)) != -1) {
        (buffer, 0, byteread);
      }
      ();
      ();
      Drawable d = (i, "src");
      return loadImageFromUrl(url);
    }else{
      URL m = new URL(url);
      InputStream i = (InputStream) ();
      Drawable d = (i, "src");
      return d;
    }
  }
}

In addition to the existing weak reference cache images, I also added local SD card cache images (these two cache methods have their own benefits. If the pictures change frequently, it is recommended to cache images in memory. If the pictures are not modified frequently, it is recommended to cache SD card)

For more information about Android related content, please check out the topic of this site:Android development introduction and advanced tutorial》、《Android multimedia operation skills summary (audio, video, recording, etc.)》、《Summary of the usage of basic Android components》、《Android View View Tips Summary》、《Android layout layout tips summary"and"Android control usage summary

I hope this article will be helpful to everyone's Android programming design.