How can I get a new image not cached in android coil?
Asked Answered
C

4

8

I am developing an android app using the Jetpack Compose with Coil ImageLoader library.

It shows a user's profile image.

I receive the profile data from the API. GET: /users/{userId}

The response contains userId and profileImgKey.

For the user profile image, Backend provides GET: /photo/{userId} API.

But the problem is that if some user update his/her profile image, other users still see the previous profile image not the new image.

Because it is cached by Coil.

If I turn-off the caching option, it may work fine. But I don't want to do it. I don't want to lose the performance benefit.

When the user update their profile image, the profileImgKey is changed. So I want to use this as a cache key.

But I don't know how to use this.

Christcross answered 15/2, 2022 at 1:58 Comment(0)
F
14

In Coil 2.0.0 working with network cache is significantly simplified.

Specify diskCacheKey and memoryCacheKey in rememberAsyncImagePainter, also key is still needed to trigger recomposition.

val context = LocalContext.current
key(key) {
    Image(
        rememberAsyncImagePainter(
            remember(url) {
                ImageRequest.Builder(context)
                    .data(url)
                    .diskCacheKey(url)
                    .memoryCacheKey(url)
                    .build()
            }
        ),
        null
    )
}

And clear cache using image loader:

val imageLoader = context.imageLoader
imageLoader.diskCache?.remove(url)
imageLoader.memoryCache?.remove(MemoryCache.Key(url))

Answer for Coil 1.4.0

Coil has a cache on two levels:

  1. For network calls Coil uses OkHttp, to access its cache you need to create it manually as shown in documentation. I think in this case it's best to store both the cache and the image loader in the DI, but you can also create a composition local to access this cache from any composable:

    val LocalCoilHttpCache = staticCompositionLocalOf<Cache> {
        error("coilHttpCache not provided")
    }
    

    Provide it in your activity/fragment:

    val cache = CoilUtils.createDefaultCache(this)
    val imageLoader = ImageLoader.Builder(this)
        .okHttpClient {
            OkHttpClient.Builder()
                .cache(cache)
                .build()
        }
        .build()
    
    setContent {
        CompositionLocalProvider(
            LocalImageLoader provides imageLoader,
            LocalCoilHttpCache provides cache,
        ) {
            // your application
        }
    }
    

    Get it in any composable

    val httpCache = LocalCoilHttpCache.current
    

    Then, regardless of where you store it in the DI or in the composition local, you can clear the necessary cache with the following code:

    val urlIterator = httpCache.urls()
    while (urlIterator.hasNext()) {
        if (urlIterator.next() == urlToRemove) {
            urlIterator.remove()
        }
    }
    
  2. After downloading an image from the network, it is converted to Bitmap. These bitmaps are cached with Memory cache, so you need to clear that as well. The easiest way, according to the documentation, is to specify a cache key, e.g. URL:

    rememberImagePainter(
        url,
        builder = {
            memoryCacheKey(MemoryCache.Key(url))
        }
    ),
    

    Then you can clean it up:

    val loader = LocalImageLoader.current
    // or out of composable
    val loader = Coil.imageLoader(context)
    // ..
    loader.memoryCache.remove(MemoryCache.Key(url))
    

The last step is to force image recomposition. You can do this with key, specifying the value to change, in your case profileImgKey should work:

key(profileImgKey) {
    Image(
        rememberImagePainter(
            // ...
Finalism answered 15/2, 2022 at 5:14 Comment(2)
Great answer - works. I had done everything already in my code, before seeing the crucial important last step here with key(..) to trigger recomposition.Countermove
I was using Uri, and it expects String from me. How do I know what exactly it should be?Hoffer
F
3

The easiest way to ensure that you get a fresh image, even when it changes is to append a query parameter to the url with a random value. For example:

someurl/user1.jpg?49610269

And done like this:

val imageUrl = "someUrl/user1.jpg?" + (0..1_000_000).random()

If your url already has query parameters, just add a new parameter that your url doesn't use.

Images in caches are reused whenever the url (including the query string) does not change. By changing the url, you force Coil to obtain the most recent image.

Flute answered 15/2, 2022 at 5:21 Comment(2)
This will just add new cache every time the image loads. This is not useful for the above scenario.Zipah
@viks But doesnt the old cache images get released as they will no longer be accessed? I'm using this technique and it seems to work as expected.Nonalcoholic
I
0

Inorder to load a new image not cached in coil try the following code on that particular Image View :

imgVw.load(imgUrl) {
    memoryCachePolicy(CachePolicy.DISABLED)
}
Infare answered 13/2, 2023 at 7:21 Comment(1)
You didn't read question carefully.Zipah
J
0

The simplest solution is updating the url:

val builder: Uri.Builder = url.buildUpon()
builder.appendQueryParameter("imageCacheKey", UUID.randomUUID().toString())
val newUri = builder.build()
Jamijamie answered 30/10, 2023 at 12:50 Comment(1)
This will just add new cache every time the image loads. This is not useful for the above scenario.Zipah

© 2022 - 2024 — McMap. All rights reserved.