1

So I recently wanted to try out the caching feature of the Picasso library, & I got into this confusing situation:

I retrieve the images' file names & paths from my web server (using Retrofit2), & I store them into ImageComponent objects (model):

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

}

So now that the loading is successful, I populate a RecyclerView with these images using Picasso. The loading and inflation process is successful, but it gets a little tricky when caching the images.

Case1: using android.util.LruCache

(For convenience, I will post the entire code of the Recyclerview's adapter. I will try to be concise)

// 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

(Notice those 2 last images, & how they show other previous images before displaying the correct one)

Result1

A few notes:

  • I ran across a problem, where the images weren't displayed at all. After some research, I found this answer which suggested binding the target to the ImageView. (worked)
  • I didn't quite understand how Picasso caches the images. Is it an automatic or manual process ? This answer states that Picasso handles this task for you. But when I actually tried it out (without the android Lrucache), no caching seemed to be done : The images were getting reloaded every time I scroll back & forth.
  • Actually I was going to post a second use case where things went even more wrong, using the Picasso's Lrucache (images were being shown randomly , & change with every scroll), but I think this post is already long enough.

My questions are:

  1. Why do I get that weird behavior ? (as shown in the attached GIF)
  2. How does this whole caching process work ? Should I (or could I) use a Lrucache when making use of Picasso ?
  3. What's the difference between the Lrucache that comes with the SDK & Picasso's ? (Performance, best use case scenarios, etc...)
Community
  • 1
  • 1
Mohammed Aouf Zouag
  • 17,042
  • 4
  • 41
  • 67

1 Answers1

0
  1. I think using both LRU cache and Picasso is causing the weird behaviour. I have used Picasso to cache Image to an Adapter, which works completely fine. you can check in here

  2. Picasso cache Image automatically when used with adapter, it will cache like this, if the child item of list/Recycler view is not visible it will stop caching the image for the respective child.So it's better to use Picasso alone with Adapter.

  3. The main usage of Picasso over LRU cache is that, Picasso is easy to use. ex : specifying Memory cache Size in Picasso.

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

Picasso also allow you to notify user with an Image when there is an error in downloading, a default holder for Imageview before loading the complete image.

Hope it helps.

Mohammed Aouf Zouag
  • 17,042
  • 4
  • 41
  • 67
HourGlass
  • 1,805
  • 1
  • 15
  • 29