Первая проблема:
- Я работаю над приложением, которое использует несколько
FragmentLists
в соответствии с индивидуальными FragmentStatePagerAdapter
. Там может быть,
потенциально значительное количество таких фрагментов, скажем, между 20 и 40.
- Каждый фрагмент представляет собой список, в котором каждый элемент может содержать текст или изображение.
- Изображения должны загружаться асинхронно из Интернета и кэшироваться во временную кэш-память, а также на SD, если доступно
- Когда Fragment исчезает с экрана, любые загрузки и текущая активность должны быть отменены (не приостановлена)
Моя первая реализация была основана на хорошо известном коде загрузчика изображений от Google. Моя проблема с этим кодом заключается в том, что он в основном создает один экземпляр AsyncTask
на изображение. Что в моем случае очень быстро убивает приложение.
Поскольку я использую пакет совместимости v4, я подумал, что использование пользовательского Loader, расширяющего AsyncTaskLoader
, поможет мне, поскольку он внутренне реализует пул потоков. Однако, к моему неприятному удивлению, если я выполню этот код несколько раз, каждый следующий вызов прервет предыдущий. Скажем, у меня есть это в моем ListView#getView
методе:
getSupportLoaderManager().restartLoader(0, args, listener);
Этот метод выполняется в цикле для каждого элемента списка, который появляется в поле зрения. И, как я уже сказал, каждый последующий вызов завершает предыдущий. Или, по крайней мере, так происходит на основе LogCat
11-03 13:33:34.910: V/LoaderManager(14313): restartLoader in LoaderManager: args=Bundle[{URL=http://blah-blah/pm.png}]
11-03 13:33:34.920: V/LoaderManager(14313): Removing pending loader: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}}
11-03 13:33:34.920: V/LoaderManager(14313): Destroying: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}}
11-03 13:33:34.920: V/LoaderManager(14313): Enqueuing as new pending loader
Тогда я подумал, что, возможно, предоставление уникального идентификатора каждому загрузчику поможет, но, похоже, это не имеет значения. В результате я получаю, казалось бы, случайные изображения, и приложение никогда не загружает даже 1/4 того, что мне нужно.
Вопрос
- Каким образом можно исправить загрузчик, чтобы он делал то, что я хочу (и есть ли способ?)
- Если нет, то каков хороший способ создания
AsyncTask
пула и возможно ли его рабочая реализация?
Чтобы дать вам представление о коде, приведите урезанную версию Loader, в которой логика загрузки / сохранения находится в отдельном классе ImageManager.
public class ImageLoader extends AsyncTaskLoader<TaggedDrawable> {
private static final String TAG = ImageLoader.class.getName();
/** Wrapper around BitmapDrawable that adds String field to id the drawable */
TaggedDrawable img;
private final String url;
private final File cacheDir;
private final HttpClient client;
/**
* @param context
*/
public ImageLoader(final Context context, final String url, final File cacheDir, final HttpClient client) {
super(context);
this.url = url;
this.cacheDir = cacheDir;
this.client = client;
}
@Override
public TaggedDrawable loadInBackground() {
Bitmap b = null;
// first attempt to load file from SD
final File f = new File(this.cacheDir, ImageManager.getNameFromUrl(url));
if (f.exists()) {
b = BitmapFactory.decodeFile(f.getPath());
} else {
b = ImageManager.downloadBitmap(url, client);
if (b != null) {
ImageManager.saveToSD(url, cacheDir, b);
}
}
return new TaggedDrawable(url, b);
}
@Override
protected void onStartLoading() {
if (this.img != null) {
// If we currently have a result available, deliver it immediately.
deliverResult(this.img);
} else {
forceLoad();
}
}
@Override
public void deliverResult(final TaggedDrawable img) {
this.img = img;
if (isStarted()) {
// If the Loader is currently started, we can immediately deliver its results.
super.deliverResult(img);
}
}
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
// At this point we can release the resources associated with 'apps'
// if needed.
if (this.img != null) {
this.img = null;
}
}
}