Использование адаптера cwac-endless для загрузки бесконечного ListView изображений

Я написал следующий код, который реализует бесконечную прокрутку ListView, содержащего ImageViews, с помощью бесконечного адаптера CWAC. Изображения извлекаются из Интернета по запросу с помощью AsyncTasks:

package com.myproject.ui.adapter;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;

import com.commonsware.cwac.endless.EndlessAdapter;

public class MyThumbsAdapter extends EndlessAdapter {

    public static class FetchThumbTask extends AsyncTask<String, Void, Bitmap> {

        private final Context mContext;
        private final BaseAdapter mAdapter;
        private final String mThumbId;
        private final View mView;

        public FetchThumbTask(Context context, BaseAdapter adapter, View view, String thumbId) {
            mContext = context;
            mAdapter = adapter;
            mView = view;
            mThumbId = thumbId;
        }

        @Override
        protected Bitmap doInBackground(String... arg0) {
            Bitmap bitmap = null;
            if (cache.get(mThumbId) == null) {
                // Fetch thumbnail
                try {
                    URL url = new URL(...);
                    InputStream is = url.openStream();
                    bitmap = BitmapFactory.decodeStream(is);
                    cache.put(mThumbId, bitmap);
                } catch (Exception e) {
                    ...
                }
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
              // Set the loaded image on the ImageView
                      ImageView imageView = (ImageView) mView.findViewById(R.id.thumb_image);
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }

    public static class MyThumbsBaseAdapter extends BaseAdapter {

        private final Context mContext;
        private final List<String> mThumbIds = new ArrayList<String>();

        public MyThumbsBaseAdapter(Context context) {
            mContext = context;
        }

        public void addThumbIds(List<String> thumbIds) {
            mThumbIds.addAll(thumbIds);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            String thumbId = mThumbIds.get(position);
            View rootView = convertView;

            if (rootView == null) {
                rootView = LayoutInflater.from(parent.getContext()).inflate(
                        R.layout.thumbnails, null);
            }
            ImageView imageView =
                    (ImageView) rootView.findViewById(R.id.doodle_thumb_image);

            Bitmap bitmap = cache.get(thumbId);
            if (bitmap == null) {
                loadThumbBitmap(rootView, thumbId);
            } else if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }

            return rootView;
        }

        @Override
        public int getCount() {
            return mThumbIds.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        private void loadThumbBitmap(View view, String thumbId) {
            new FetchThumbTask(mContext, this, view, thumbId)
                .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    }

    static {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        cache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // The cache size will be measured in kilobytes rather than
                // number of items.
                return bitmap.getByteCount() / 1024;
            }
        };
    }

    private static final LruCache<String, Bitmap> cache;

    private List<String> mThumbIdsCache;

    public MyThumbsAdapter(Context context) {
        super(new MyThumbsBaseAdapter(context));
    }

    @Override
    protected View getPendingView(ViewGroup parent) {
        return LayoutInflater.from(parent.getContext())
                .inflate(R.layout.loading_thumb, null);
    }

    @Override
    protected boolean cacheInBackground() throws Exception {
        JsonReader reader = // Retrieve thumb ids list from server
        mThumbIdsCache = // Returned thumb ids list
        return true;
    }

    @Override
    protected void appendCachedData() {
        MyThumbsBaseAdapter adapter = (MyThumbsBaseAdapter) getWrappedAdapter();
        adapter.addThumbIds(mThumbIdsCache);
    }
}

Я использую LruCache для кэширования растровых изображений, загруженных из Интернета. Проблема в том, что при тестировании на Nexus 7 я наблюдаю множество промахов кеша, у которого должно быть много доступной памяти. Это приводит к тому, что изображения появляются на месте, когда я прокручиваю ListView вверх/вниз.

Хуже того, я видел сбой приложения с ошибками OutOfMemory, но я не могу последовательно воспроизвести.

Что я здесь делаю неправильно? Должен ли я запускать отдельные AsyncTask для каждого изображения?

РЕДАКТИРОВАТЬ: я также должен упомянуть, что изображения, которые я загружаю, предварительно масштабируются.


person Simian    schedule 12.03.2013    source источник


Ответы (1)


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

Другой вариант: вы можете использовать универсальный загрузчик изображений, который поможет вам буферизовать изображение в диск (например, SD card или Internal memory вашего приложения). Так что проблема Out of Memory может быть решена.

Кроме того, чтобы лучше отображать растровые изображения, Эффективное отображение растровых изображений полезный.

Изменить:

Используйте следующую конфигурацию для вашего приложения. Это будет кэшировать изображения внутри кэш-папки приложения.

File cacheDir = new File(this.getCacheDir(), "cwac");
if (!cacheDir.exists())
    cacheDir.mkdir();

ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
            CWAC.this)
            .threadPoolSize(5)
            .threadPriority(Thread.MIN_PRIORITY + 3)
            .denyCacheImageMultipleSizesInMemory()
            // .memoryCache(new UsingFreqLimitedMemoryCache(2000000)) // You
            // can pass your own memory cache implementation
            .memoryCacheSize(1048576 * 10)
            // 1MB=1048576 *declare 20 or more size if images are more than
            // 200
            .discCache(new UnlimitedDiscCache(cacheDir))
            // You can pass your own disc cache implementation
            //.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
            .build();
person Chintan Rathod    schedule 12.03.2013
comment
Спасибо! РЕДАКТИРОВАТЬ: я также должен упомянуть, что изображения, которые я загружаю, предварительно масштабируются. - person Simian; 12.03.2013
comment
Это я пропустил .. извините, что не посмотрел. - person Chintan Rathod; 12.03.2013
comment
К сожалению, кажется, что использование ImageLoader на самом деле работает хуже, чем мое решение LruCache выше, даже после настройки его memoryCacheSize. - person Simian; 12.03.2013
comment
проверьте intexsoft.com/blog/item/68 -universal-image-loader-part-1.html учебник по загрузчику изображений. Хорошая и понятная вся библиотека. Вы также можете использовать кэширование LRU и другие вещи. - person Chintan Rathod; 12.03.2013
comment
ImageLoader работает очень хорошо после добавления некоторых дополнительных настроек. Одна оставшаяся проблема заключается в том, что я вижу перерасход. Изображения иногда мигают после появления в поле зрения, как если бы они рисовались несколько раз. - person Simian; 12.03.2013
comment
вид изображения обновляется, приятель. Или вы также можете использовать ViewHolder для удаления создания нескольких представлений. - person Chintan Rathod; 12.03.2013