Android - Стратегия пула потоков и можно ли использовать Loader для ее реализации?

Во-первых, проблема:

  • Я работаю над приложением, которое использует несколько FragmentList {{1} } в настроенном FragmentStatePagerAdapter . Может быть потенциально значительное количество таких фрагментов, скажем, от 20 до 40.
  • Каждый фрагмент представляет собой список, в котором каждый элемент может содержать текст или изображение.
  • Изображения должны быть загружены асинхронно из Интернета и кэшированы во временный кеш памяти, а также на SD, если он доступен
  • Когда фрагмент уходит с экрана, любые загрузки и текущая активность должны быть отменены (не приостановлены)

Моя Первая реализация последовала за хорошо известным кодом загрузчика изображений от Google. Моя проблема с этим кодом в том, что он в основном создает один экземпляр AsyncTask для каждого изображения. Что в моем случае убивает приложение очень быстро.

Поскольку я использую пакет совместимости с v4, я подумал, что использование специального загрузчика, расширяющего 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 {
        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;
        }

    }

}

7
задан Bostone 3 November 2011 в 20:47
поделиться