2

I am trying to switch between the original and transformed bitmaps using Picasso. The problem is the first time the original is loaded it seems to be cached, but when I load the transformed image then it seems to reload the image again and not using the cache. The same url is used to fetch the image. It happens only the first time for the original and transformed and then the cache is used.

My expectation that Picasso should automatically reuse the original image cashed to apply the transform and reload it with no delay. Maybe I am missing something.

Here is the code of image loading.

 private fun loadOriginalImage(i: Product, productImage: ImageView) {
    Picasso.get().load(getProductUrl(i.id)).placeholder(R.color.light_grey)
        .error(R.color.light_grey).fit().centerCrop().into(productImage)
}


 private fun loadGreyedImage(i: Product, productImage: ImageView) {
    Picasso.get().load(getProductUrl(i.id)).placeholder(R.color.light_grey)
        .error(R.color.light_grey).fit().centerCrop().transform(GrayScaleTransform()).into(productImage)
}

Picasso version implementation 'com.squareup.picasso:picasso:2.71828'

Rainmaker
  • 10,294
  • 9
  • 54
  • 89

2 Answers2

1

Was it memory cache or disk cache? How did you verify that the cache wasn't used indeed?

There are some options here(considering MemoryPolicy and NetworkPolicy were not modified and you didn't replace standard okHTTP3 client):

  • Image was pushed out of memory cache between these two calls(cache too small, invalidate call etc)
  • Disk cache is under control of HTTP client and has nothing to do with Picasso so improperly set up http headers could cause this(but in that case original image should be removed from the memory cache already)

Turning on indicators and logging can give more information on what's going on:

Picasso  
.with(context)
.setIndicatorsEnabled(true)
.setLoggingEnabled(true)

Also, if there's a possibility to do these two calls only, you can execute them and grab the snapshot data from the memory cache to check its size, hits and misses etc.:

StatsSnapshot stats = Picasso.with(context).getSnapshot();  
Log.d("stats", stats.toString());  

That possibly can give more debug information to consider e.g. were there exactly two calls to the cache or not, was there a miss etc

Edit: is key() function properly implemented in your transform?

Sputnik
  • 328
  • 1
  • 9
  • I am not manipulating cache myself in my case. So I guess Picasso should be using lru cache under the hood. What tells me that the cache was not used is the fact that I see the placeholder for the first time when bitmap is being loaded and then I don't which indicated the cache is reused. I will try your suggestions – Rainmaker Jun 05 '19 at 20:21
  • Ah, in that case you possibly see the placeholder when you load it for the first time because it takes some time to apply transformation to original image(even if it was cached previously). After that cache already contains transformed image so Picasso skips transform step and loads the image into view instantly(kinda) – Sputnik Jun 05 '19 at 20:33
  • makes sense, do you think there is a solution to this problem? my expectation is for it to happen faster than 1-2 secs it takes now – Rainmaker Jun 05 '19 at 20:35
  • ¯\\_(ツ)_/¯ Depends on specific use case. Check the size of original image, maybe it's too big for mobile anyway and can be reduced(smaller image - smaller transformation time), add callback to the first load of original image so it could start transform and put modified image into the cache immediately after etc. – Sputnik Jun 05 '19 at 20:49
  • the callback idea may be the solution. So how would it look codewise? I will check the size but I am pretty sure it is mobile adapted. – Rainmaker Jun 05 '19 at 20:54
  • you can pass a Callback object as a second parameter for into, so like this: `Picasso.get().load(getProductUrl(i.id)).placeholder(R.color.light_grey).error(R.color.light_grey).fit().centerCrop().into(productImage, new Callback { onSuccess() { Picasso.get().load(getProductUrl(i.id)).placeholder(R.color.light_grey).error(R.color.light_grey).fit().centerCrop().transform(GrayScaleTransform()).fetch()} })`. In that case next time you call it will be from the cache – Sputnik Jun 05 '19 at 21:00
  • thanks for your help, I figured out a solution that suited my needs – Rainmaker Jun 09 '19 at 16:50
1

After enabling logs I verified that the original image was cached indeed and as @Sputnik suggested the problem seemed to be caused by a delay in creating a transformed bitmap from the original image cache.

But the solution of pre-caching transformed images is not the best one imo. First of all, there is quite some images and greyscaled ones are required only when user clicks on the image in the recycler. It may never happen but we already cached double size of images just in case.

So after playing with Picasso and logs the solution that works for me was to use the original image as a placeholder.

so instead of

    Picasso.get().load(getProductUrl(i.id)).placeholder(R.color.light_grey)
    .error(R.color.light_grey).fit().centerCrop().transform(GrayScaleTransform()).into(productImage)

I did this

    Picasso.get().load(getProductUrl(i.id)).placeholder(productImage.drawable)
    .error(R.color.light_grey).fit().centerCrop().transform(GrayScaleTransform()).into(productImage)

This way my placeholder is the original image and it make the transition smooth and gets rid of potential overhead with caching all the transformed images.

Rainmaker
  • 10,294
  • 9
  • 54
  • 89