Skip to content

Commit

Permalink
feat(image): [android] adding force-cache cache control option (#47426
Browse files Browse the repository at this point in the history
)

Summary:
This PR follows up on #47182 and #47348 by adding `force-cache`, the final missing option to align caching controls with the existing behavior on iOS.

Local caching behavior remains unchanged: if a cached image is available locally, it will be returned; otherwise, a network request will be made.

When an image request is sent over the network, the `force-cache` option sent from the sent fJS side will now use the `okhttp3.CacheControl.FORCE_CACHE` directive.

## Changelog:

[ANDROID] [ADDED] - Image `force-cache` caching control option

Pull Request resolved: #47426

Test Plan:
New example added to the RNTester under the cache policy examples. Then inspecting that the cache control is set correctly before sending it in the `okhttp3.Request` builder.

```kt
FLog.w("ReactNative", "fetching uri: %s, with cacheControl: %s", uri, cacheControlBuilder.build().toString())
// fetching uri: https:...png?cacheBust=force-cache, with cacheControl: no-store, max-stale=2147483647, only-if-cached
```

This case was a bit more tricky to test in terms of e2e as it would involve some caching in the server as well, I'm open to suggestions to make this more complete.

Reviewed By: javache

Differential Revision: D65490360

Pulled By: Abbondanzo

fbshipit-source-id: f807a9793f85caea39c59a370d057b9a1d450a78
  • Loading branch information
mateoguzmana authored and facebook-github-bot committed Nov 12, 2024
1 parent c69e330 commit a0be88f
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 8 deletions.
2 changes: 0 additions & 2 deletions packages/react-native/Libraries/Image/ImageSource.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ export interface ImageURISource {
* its age or expiration date. If there is no existing data in the cache corresponding
* to a URL load request, no attempt is made to load the data from the originating source,
* and the load is considered to have failed.
*
* @platform ios (for `force-cache`)
*/
cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached' | undefined;
/**
Expand Down
2 changes: 0 additions & 2 deletions packages/react-native/Libraries/Image/ImageSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ export interface ImageURISource {
* its age or expiration date. If there is no existing data in the cache corresponding
* to a URL load request, no attempt is made to load the data from the originating source,
* and the load is considered to have failed.
*
* @platform ios (for `force-cache`)
*/
+cache?: ?('default' | 'reload' | 'force-cache' | 'only-if-cached');

Expand Down
1 change: 1 addition & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -3334,6 +3334,7 @@ public final class com/facebook/react/modules/fresco/FrescoModule$Companion {

public final class com/facebook/react/modules/fresco/ImageCacheControl : java/lang/Enum {
public static final field DEFAULT Lcom/facebook/react/modules/fresco/ImageCacheControl;
public static final field FORCE_CACHE Lcom/facebook/react/modules/fresco/ImageCacheControl;
public static final field ONLY_IF_CACHED Lcom/facebook/react/modules/fresco/ImageCacheControl;
public static final field RELOAD Lcom/facebook/react/modules/fresco/ImageCacheControl;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public enum class ImageCacheControl {
* be used to satisfy a URL load request.
*/
RELOAD,
/**
* The existing cache data will be used to satisfy a request, regardless of its age or expiration
* date. If there is no existing data in the cache corresponding to a URL load request, the data
* is loaded from the originating source.
*/
FORCE_CACHE,
/**
* The existing cache data will be used to satisfy a request, regardless of its age or expiration
* date. If there is no existing data in the cache corresponding to a URL load request, no attempt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.facebook.imagepipeline.backends.okhttp3.OkHttpNetworkFetcher
import com.facebook.imagepipeline.producers.NetworkFetcher
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.modules.network.OkHttpCompat
import java.util.concurrent.TimeUnit
import okhttp3.CacheControl
import okhttp3.OkHttpClient
import okhttp3.Request
Expand All @@ -35,21 +36,26 @@ internal class ReactOkHttpNetworkFetcher(private val okHttpClient: OkHttpClient)
fetchState.submitTime = SystemClock.elapsedRealtime()
val uri = fetchState.uri
var requestHeaders: Map<String, String>? = null
val cacheControlBuilder = CacheControl.Builder().noStore()
val cacheControlBuilder = CacheControl.Builder()
if (fetchState.context.imageRequest is ReactNetworkImageRequest) {
val networkImageRequest = fetchState.context.imageRequest as ReactNetworkImageRequest
requestHeaders = getHeaders(networkImageRequest.headers)
when (networkImageRequest.cacheControl) {
ImageCacheControl.RELOAD -> {
cacheControlBuilder.noCache()
cacheControlBuilder.noStore().noCache()
}
ImageCacheControl.FORCE_CACHE -> {
cacheControlBuilder.maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
}
ImageCacheControl.ONLY_IF_CACHED -> {
cacheControlBuilder.onlyIfCached()
cacheControlBuilder.onlyIfCached().maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
}
ImageCacheControl.DEFAULT -> {
// No-op
cacheControlBuilder.noStore()
}
}
} else {
cacheControlBuilder.noStore()
}
val headers = OkHttpCompat.getHeadersFromMap(requestHeaders)
val request =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ public class ReactImageView(
null,
"default" -> ImageCacheControl.DEFAULT
"reload" -> ImageCacheControl.RELOAD
"force-cache" -> ImageCacheControl.FORCE_CACHE
"only-if-cached" -> ImageCacheControl.ONLY_IF_CACHED
else -> ImageCacheControl.DEFAULT
}
Expand Down
12 changes: 12 additions & 0 deletions packages/rn-tester/js/examples/Image/ImageExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,18 @@ function CacheControlAndroidExample(): React.Node {
key={reload}
/>
</View>
<View style={styles.leftMargin}>
<RNTesterText style={styles.resizeModeText}>Force-cache</RNTesterText>
<Image
source={{
uri: fullImage.uri + '?cacheBust=force-cache',
cache: 'force-cache',
}}
style={styles.base}
key={reload}
onError={e => console.log(e.nativeEvent.error)}
/>
</View>
<View style={styles.leftMargin}>
<RNTesterText style={styles.resizeModeText}>
Only-if-cached
Expand Down

0 comments on commit a0be88f

Please sign in to comment.