Безопасный, стандартный способ загрузки изображений в ListView в другом потоке? - PullRequest
1 голос
/ 22 мая 2010

Прежде чем задать этот вопрос, я искал и читал эти: Ленивая загрузка изображений в ListView Android - проблема с отложенной загрузкой изображений в ListView

Моя проблема в том, что у меня есть ListView, где:

  • Каждая строка содержит ImageView, чей содержимое должно быть загружено из Интернет
  • Вид каждой строки перерабатывается как в Список ApiDemo14

Что я хочу в конечном итоге:

  • Загрузка изображений лениво, только когда пользователь свитки к ним
  • Загрузка изображений в разные темы для поддерживать отзывчивость

Мой текущий подход:

  • В методе адаптера getView(), кроме настраивая другие дочерние представления, я запускаю новый поток, который загружает растровое изображение из интернет. Когда что нить загрузки После завершения возвращается Bitmap, который будет установлен на ImageView (я делаю это с помощью AsyncTask или Handler).
  • Поскольку я перерабатываю 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 объекты для отличается Activities, то когда Activity уничтожается, все его AsyncTask будет отменено. Это слишком неловко?

Во всяком случае, мне интересно, есть ли безопасный и стандартный способ сделать это в Android. Кроме того, я не знаю iPhone, но есть ли там похожая проблема, и есть ли у них стандартный способ решения этой задачи?

Большое спасибо.

1 Ответ

4 голосов
/ 22 мая 2010

Я решаю эту проблему, поддерживая WeakHashMap, который запоминает последнее растровое изображение, которое я хочу установить для этого представления.

Я применил подход, прикрепляя URL нужного изображения к ImageView через setTag().Затем, когда я загрузил изображение, я дважды проверил URL ImageView - если он отличается от того, который я только что скачал, я не обновляю ImageView, потому что он был переработан.Я просто кеширую его.

Есть ли риск, когда загрузочный поток перезванивает ImageView позже, когда действие уже уничтожено?

Я не в курселюбой риск, кроме немного потраченного впустую процессорного времени и полосы пропускания (и, следовательно, батареи).

Вместо того, чтобы превращать ImageLoader в подобный элемент, я думаю о создании отдельных объектов ImageLoader длядругие действия, затем, когда действие будет уничтожено, все его AsyncTask будут отменены.Это слишком неловко?

Отменить AsyncTask не так уж легко, если он уже запущен.Я бы просто позволил этому дойти до конца.

В идеале, избегать одиночных игр.Либо используйте Service, либо передайте ImageLoader следующему экземпляру своей активности с помощью onRetainNonConfigurationInstance() (например, isFinishing() - это false в onDestroy(), так что это ротация).

...