Posted at

VolleyのImageLoaderをListViewで使うときの注意点

More than 5 years have passed since last update.


結論

キャンセル処理はちゃんと入れよう。


Volley

Android開発で、ネットワーク通信周りの処理を簡単に実装できるすごく便利なライブラリ。JSONの取得とか、画像の非同期取得とか簡単に実装できる。


ListViewの実装パターン

ListView使う時はviewのインスタンスを再利用したり、ViewHolder使うのが定石になってます。

adapter#getView()はだいたいこんな実装


sample

private class ViewHolder {

ImageView imageView;
TextView textView;
}

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

if (convertView == null) {
convertView = mInflater.inflate(R.layout.list, null);

ImageView imageView = (ImageView)convertView.findViewById(R.id.image);
TextView textView = (TextView)convertView.findViewById(R.id.text);

holder = new ViewHolder();
holder.imageView = imageView;
holder.textView = textView;

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

// 画像やテキストの設定

return convertView;
}



画像をVolleyのImageLoaderを使って非同期で読み込む

VolleyのImageLoaderを使うと簡単に画像を非同期で読み込むことができます。

使い方はネットワーク通信用ライブラリVolleyを使いこなすに詳しく乗っているので見てください。


惜しい実装パターン

画像読み込みの処理だけ加えた実装


sample

private ImageLoader mImageLoader;

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

if (convertView == null) {
// 上と一緒
} else {
holder = (ViewHolder)convertView.getTag();
}

String imageUrl = "<画像のurl>";
ImageListener listener = ImageLoader.getImageListener(holder.imageView,
R.drawable.progress, R.drawable.error);
mImageLoader.get(imageUrl, listener);

return convertView;
}


これで、ImageViewに非同期で読み込んだ画像がセットされます。

ただ、これだけだとちょっと問題があって、表示された順に画像読み込みのリクエストが投げられるので、ListViewを高速にスクロールすると、どんどんリクエストが溜まっていきます。

リクエストは順番に処理されるので、通りすぎて表示する必要のなくなった画像のリクエストが終わるまで、次のリクエストが処理されなくなり、ロード時間が長くなります。

また、viewを再利用するようになっているので、違うポジションの画像が表示されたりする問題も発生します。

そこで次のように、適切にリクエストのキャンセル処理を入れてあげます。


キャンセル処理をいれたパターン


sample

private ImageLoader mImageLoader;

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

if (convertView == null) {
// 上と一緒
} else {
holder = (ViewHolder)convertView.getTag();
}

// リクエストのキャンセル処理
ImageContainer imageContainer = (ImageContainer)holder.imageView.getTag();
if (imageContainer != null) {
imageContainer.cancelRequest();
}

String imageUrl = "<画像のurl>";
ImageListener listener = ImageLoader.getImageListener(holder.imageView,
R.drawable.progress, R.drawable.error);
holder.imageView.setTag(mImageLoader.get(imageUrl, listener));

return convertView;
}


ImageLoader#get()の返り値のImageContainerはリクエストを管理しているので、このインスタンスをImageViewのタグに設定して保持しておきます。

Viewの再利用時にImageViewからImageContainerのインスタンスを取得し、リクエストのキャンセルを行っています。

これで、必要のなくなったリクエストはキャンセルされて、無駄な処理を省くことができます。