1

この質問をする前に、次のものを検索して読みました: ListView Android での画像の遅延読み込み - ListView への画像の遅延読み込みに関する問題

私の問題は、私が持っていることですListView

  • 各行には が含まれておりImageView、そのコンテンツはインターネットからロードされます
  • 各行のビューは、ApiDemo の List14 のようにリサイクルされます

最終的に欲しいもの:

  • ユーザーがスクロールしたときにのみ、画像を遅延して読み込みます
  • 応答性を維持するために、異なるスレッドに画像をロードします

私の現在のアプローチ:

  • アダプターのgetView()メソッドでは、他の子ビューをセットアップするのとは別に、インターネットからビットマップを読み込む新しいスレッドを起動します。その読み込みスレッドが終了すると、Bitmapに設定される が返されます (またはImageViewを使用してこれを行います)。AsyncTaskHandler
  • 私は s をリサイクルするのでImageView、最初に でビューを設定しBitmap#1、後でBitmap#2ユーザーが下にスクロールしたときに に設定したい場合があります。Bitmap#1読み込みよりも時間がかかる場合がBitmap#2あるため、ビューが上書きBitmap#2される可能性があります。WeakHashMapそのビューに最後Bitmapに設定したいものを記憶する を維持することで、これを解決します。

以下は、私の現在のアプローチの擬似コードです。わかりやすくするために、キャッシングなどの他の詳細は省略しました。

public class ImageLoader {

    // keeps track of the last Bitmap we want to set for this ImageView
    private static final WeakHashMap<ImageView, AsyncTask> assignments
                                    = new WeakHashMap<ImageView, AsyncTask>();

    /** Asynchronously sets an ImageView to some Bitmap loaded from the internet */
    public static void setImageAsync(final ImageView imageView, final String imageUrl) {
        // cancel whatever previous task
        AsyncTask oldTask = assignments.get(imageView);
        if (oldTask != null) {
            oldTask.cancel(true);
        }

        // prepare to launch a new task to load this new image
        AsyncTask<String, Integer, Bitmap> newTask = new AsyncTask<String, Integer, Bitmap>() {

            protected void onPreExecute() {
                // set ImageView to some "loading..." image
            }

            protected Bitmap doInBackground(String... urls) {
                return loadFromInternet(imageUrl);
            }

            protected void onPostExecute(Bitmap bitmap) {
                // set Bitmap if successfully loaded, or an "error" image
                if (bitmap != null) {
                    imageView.setImageBitmap(bitmap);
                } else {
                    imageView.setImageResource(R.drawable.error);
                }
            }
        };
        newTask.execute();

        // mark this as the latest Bitmap we want to set for this ImageView
        assignments.put(imageView, newTask);
    }

    /** returns (Bitmap on success | null on error) */
    private Bitmap loadFromInternet(String imageUrl) {}
}

まだ問題があります: 一部の画像がまだロードされている間にアクティビティが破棄された場合はどうなりますか?

  • アクティビティが既に破棄されているときに、読み込みスレッドが後で ImageView にコールバックするリスクはありますか?
  • さらに、AsyncTask下にいくつかのグローバルスレッドプールがあるため、長いタスクが不要になったときにキャンセルされない場合、ユーザーが表示
    しないものをロードするのに時間を無駄にする可能性があります. このことをグローバルに保持するという私の現在の設計はあまりにも醜く、最終的には私の理解を超えたリークが発生する可能性があります. ImageLoaderこのようなシングルトンを作成する代わりに 、実際ImageLoaderに異なる の個別のオブジェクトを作成しActivitiesActivityが破棄されると、そのすべて AsyncTaskがキャンセルされることを考えています。これはあまりにも厄介ですか?

とにかく、Androidでこれを行う安全で標準的な方法があるのだろうか. さらに、私はiPhoneを知りませんが、同様の問題があり、この種のタスクを行う標準的な方法はありますか?

どうもありがとう。

4

1 に答える 1

4

そのビューに設定したい最後のビットマップを記憶する WeakHashMap を維持することで、これを解決します。

目的の画像の URL をImageViewviaに添付するアプローチを取りましたsetTag()。次に、画像をダウンロードしたら、URL を再確認します。ImageViewダウンロードしたばかりの URL と異なる場合は、ImageView再利用されているため、更新しません。私はそれをキャッシュするだけです。

アクティビティが既に破棄されている場合、読み込みスレッドが後で ImageView にコールバックするリスクはありますか?

私は、CPU 時間と帯域幅 (したがってバッテリー) が少し無駄になる以外に、リスクを認識していません。

このように ImageLoader をシングルトンにする代わりに、実際にさまざまなアクティビティに対して個別の ImageLoader オブジェクトを作成することを考えています。その後、アクティビティが破棄されると、そのすべての AsyncTask がキャンセルされます。これはあまりにも厄介ですか?

すでに実行されている場合、キャンセルするのAsyncTaskは非常に簡単ではありません。私はそれを最後まで実行させます。

理想的には、シングルトンは避けてください。を使用するか、アクティビティの次のインスタンスに をService渡します(たとえば、is inなので、これはローテーションです)。ImageLoaderonRetainNonConfigurationInstance()isFinishing()falseonDestroy()

于 2010-05-22T16:48:03.613 に答える