Прежде чем задать этот вопрос, я искал и читал следующие: Ленивая загрузка изображений в ListView Android: проблема с ленивой загрузкой изображений в ListView
Моя проблема в том, что у меня есть ListView
, где:
- Каждая строка содержит
ImageView
, содержимое которого должно быть загружено из Интернета. - Представление каждой строки перерабатывается, как в списке ApiDemo14.
Что я хочу в конечном итоге:
- Загружайте изображения лениво, только когда пользователь прокручивает их
- Загружайте изображения в разные потоки, чтобы поддерживать отзывчивость
Мой текущий подход:
- В методе
getView()
адаптера, помимо настройки других дочерних представлений, я запускаю новый поток, который загружает растровое изображение из Интернета. Когда этот поток загрузки завершается, он возвращаетBitmap
для установки вImageView
(я делаю это, используяAsyncTask
илиHandler
). - Поскольку я перерабатываю
ImageView
s, может случиться так, что я сначала хочу установить представление с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, но есть ли там подобная проблема и есть ли у них стандартный способ сделать такую задачу?
Большое спасибо.