Search code examples
androidandroid-jetpack-composecoil

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


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.


Solution

  • 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(
                // ...