Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
114
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

@gari_jp

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

結論

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

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のインスタンスを取得し、リクエストのキャンセルを行っています。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
114
Help us understand the problem. What are the problem?