Пикассо изображения загрузки и кэширование поведения

Поэтому недавно я хотел опробовать функцию кэширования в библиотеке Пикассо, и я попал в эту запутанную ситуацию:

Я получаю имена и пути к файлам изображений с моего веб-сервера (используя Retrofit2) и сохраняю их в ImageComponent объекты (модель):

public class ImageComponent {

    private int id; // 'id' in database
    private String filename; // image name
    private String path; // image path in server storage
    private Bitmap bitmap;

    // Overloaded constructor

    // Getters & setters

}

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

Случай 1: использование android.util.LruCache

(Для удобства выложу весь код Recyclerview Адаптер. Постараюсь быть лаконичным)

// imports
import android.util.LruCache;

public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {

    private Context mContext; // Activity's context
    private List<ImageComponent> mImages; // The imageComponents to display

    // The contreversial, infamous cache
    private LruCache<Integer, Bitmap> mImageCache; 

    public ImageAdapter(Context context, List<ImageComponent> images) {
        mContext = context;
        mImages = images;

        // Provide 1/8 of available memory to the cache
        final int maxMemory = (int)(Runtime.getRuntime().maxMemory() /1024);
        final int cacheSize = maxMemory / 8;
        mImageCache = new LruCache<>(cacheSize);
    }

    @Override
    public ImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // Nothing special
    }

    @Override
    public void onBindViewHolder(final ImageAdapter.ViewHolder holder, final int position) {
        // Current ImageComponent
        ImageComponent imageComponent = mImages.get(position);

        // Full image path in server storage
        String imagePath = Constants.SERVER_IP_ADDRESS + Constants.UPLOADS_DIRECTORY
                + imageComponent.getPath();

        // Display the file's name
        holder.text.setText(imageComponent.getFilename());

        final ImageView imageView = holder.image;

        // Get bitmap from cache, check if it exists or not
        Bitmap bitmap = mImageCache.get(imageComponent.getId());
        if (bitmap != null) {
            Log.i("ADAPTER", "BITMAP IS NOT NULL - ID = " + imageComponent.getId());
            // Image does exist in cache
            holder.image.setImageBitmap(imageComponent.getBitmap());
        }
        else {
            Log.i("ADAPTER", "BITMAP IS NULL");

            // Callback to retrieve image, cache it & display it
            final Target target = new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    ImageComponent img = mImages.get(position);

                    // Display image
                    holder.image.setImageBitmap(bitmap);

                    // Cache the image
                    img.setBitmap(bitmap);
                    mImages.set(position, img);
                    mImageCache.put(img.getId(), bitmap);
                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            };

            // Tag the target to the view, to keep a strong reference to it
            imageView.setTag(target);
            // Magic
            Picasso.with(mContext)
                    .load(imagePath)
                    .into(target);
        }
    }

    @Override
    public int getItemCount() {
        return mImages.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        ImageView image;
        TextView text;

        // Constructor & view binding, not that special
    }
}

result1

(Обратите внимание на эти 2 последних изображения и то, как они показывают другие предыдущие изображения перед отображением правильного)

Result1

Несколько заметок:

  • Я столкнулся с проблемой, когда изображения вообще не отображались. После некоторого исследования я нашел этот ответ, который предложил связать target к ImageView, (работал)
  • Я не совсем понял, как Пикассо кэширует изображения. Это автоматический или ручной процесс? В этом ответе говорится, что Пикассо выполняет эту задачу для вас. Но когда я действительно попробовал (без андроида Lrucache), кеширование, похоже, не выполнялось: изображения перезагружались каждый раз, когда я прокручивал назад и вперед.
  • На самом деле я собирался опубликовать второй вариант использования, когда все пошло еще хуже, используя Пикассо Lrucache (изображения показывались случайным образом, и меняются с каждым свитком), но я думаю, что этот пост уже достаточно длинный.

Мои вопросы:

  1. Почему я получаю это странное поведение? (как показано в прикрепленном GIF)
  2. Как работает весь этот процесс кэширования? Должен ли я (или мог ли я) использовать Lrucache при использовании Пикассо?
  3. Какая разница между Lrucache что идет с SDK & Picasso? (Производительность, лучшие сценарии использования и т. Д.)

1 ответ

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

  2. Кэша Picasso Изображение автоматически при использовании с адаптером, оно будет кэшироваться следующим образом, если дочерний элемент представления списка /Recycler не виден, он прекратит кэширование изображения для соответствующего дочернего элемента. Поэтому лучше использовать Picasso отдельно с Adapter.

  3. Основное использование Picasso поверх LRU кеша в том, что Picasso прост в использовании. пример: указание размера кэша памяти в Picasso.

    Picasso picasso = new Picasso.Builder(context)
                              .memoryCache(new LruCache(250))
                              .build();
    

Picasso также позволяет уведомлять пользователя с помощью Image при возникновении ошибки при загрузке - держателя по умолчанию для Imageview перед загрузкой полного изображения.

Надеюсь, поможет.

Другие вопросы по тегам