feat(image): [android] adding only-if-cached cache control option (#47348)

Summary:
Following up from https://github.com/facebook/react-native/issues/47182, as basic caching control is already in place in Android, it can be extended to include the `only-if-cached` option.

We check whether the image is in the cache. If it is, we proceed to load it. Otherwise, we do nothing.

## Changelog:

[ANDROID] [ADDED] - Image `only-if-cached` cache control option

Pull Request resolved: https://github.com/facebook/react-native/pull/47348

Test Plan:
In the `rn-tester`, I added a third example for Android where the third image will never be loaded as the cache policy is set to `only-if-cached` and the image has not been loaded before.

<details>
<summary>Video demonstrating how the `only-if-cached` options behaves</summary>

https://github.com/user-attachments/assets/45669e81-5414-4103-8931-138bffa81447

</details>

<details>
<summary>Error from image not found in cache example</summary>

<img width="807" alt="image" src="https://github.com/user-attachments/assets/6b79d811-1809-437c-b2fe-c86d3da7c58d">

</details>

Reviewed By: rshest

Differential Revision: D65384639

Pulled By: Abbondanzo

fbshipit-source-id: f4a72694f45eb3d7097c350f4a4008a0abf0a1ab
This commit is contained in:
Mateo Guzmán authored and Facebook GitHub Bot committed 2024-11-04 08:37:59 -08:00
1 parent 91df5a4a0f
commit dc9db01665
7 files changed
+42 -5

No files matched your search

+1 -1
View File
@@ -51,7 +51,7 @@ export interface ImageURISource {
* 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` and `only-if-cached`)
* @platform ios (for `force-cache`)
*/
cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached' | undefined;
/**
+1 -1
View File
@@ -66,7 +66,7 @@ export interface ImageURISource {
* 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` and `only-if-cached`)
* @platform ios (for `force-cache`)
*/
+cache?: ?('default' | 'reload' | 'force-cache' | 'only-if-cached');
@@ -3379,6 +3379,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 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;
public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/modules/fresco/ImageCacheControl;
@@ -15,4 +15,11 @@ 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, no attempt
* is made to load the data from the originating source, and the load is considered to have
* failed.
*/
ONLY_IF_CACHED,
}
@@ -43,6 +43,9 @@ internal class ReactOkHttpNetworkFetcher(private val okHttpClient: OkHttpClient)
ImageCacheControl.RELOAD -> {
cacheControlBuilder.noCache()
}
ImageCacheControl.ONLY_IF_CACHED -> {
cacheControlBuilder.onlyIfCached()
}
ImageCacheControl.DEFAULT -> {
// No-op
}
@@ -41,6 +41,7 @@ import com.facebook.imagepipeline.image.ImageInfo
import com.facebook.imagepipeline.postprocessors.IterativeBoxBlurPostProcessor
import com.facebook.imagepipeline.request.BasePostprocessor
import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequest.RequestLevel
import com.facebook.imagepipeline.request.ImageRequestBuilder
import com.facebook.imagepipeline.request.Postprocessor
import com.facebook.react.bridge.ReactContext
@@ -311,10 +312,18 @@ public class ReactImageView(
null,
"default" -> ImageCacheControl.DEFAULT
"reload" -> ImageCacheControl.RELOAD
"only-if-cached" -> ImageCacheControl.ONLY_IF_CACHED
else -> ImageCacheControl.DEFAULT
}
}
private fun computeRequestLevel(cacheControl: ImageCacheControl): RequestLevel {
return when (cacheControl) {
ImageCacheControl.ONLY_IF_CACHED -> RequestLevel.DISK_CACHE
else -> RequestLevel.FULL_FETCH
}
}
public fun setDefaultSource(name: String?) {
val newDefaultDrawable = instance.getResourceDrawable(context, name)
if (defaultImageDrawable != newDefaultDrawable) {
@@ -426,6 +435,7 @@ public class ReactImageView(
val imageSource = this.imageSource ?: return
val uri = imageSource.uri
val cacheControl = imageSource.cacheControl
val requestLevel = computeRequestLevel(cacheControl)
val postprocessorList = mutableListOf<Postprocessor>()
iterativeBoxBlurPostProcessor?.let { postprocessorList.add(it) }
@@ -445,6 +455,7 @@ public class ReactImageView(
.setResizeOptions(resizeOptions)
.setAutoRotateEnabled(true)
.setProgressiveRenderingEnabled(progressiveRenderingEnabled)
.setLowestPermittedRequestLevel(requestLevel)
if (resizeMethod == ImageResizeMethod.NONE) {
imageRequestBuilder.setDownsampleOverride(DownsampleMode.NEVER)
@@ -659,6 +659,20 @@ function CacheControlAndroidExample(): React.Node {
key={reload}
/>
</View>
<View style={styles.leftMargin}>
<RNTesterText style={styles.resizeModeText}>
Only-if-cached
</RNTesterText>
<Image
source={{
uri: fullImage.uri + '?cacheBust=only-if-cached',
cache: 'only-if-cached',
}}
style={styles.base}
key={reload}
onError={e => console.log(e.nativeEvent.error)}
/>
</View>
</View>
<View style={styles.horizontal}>
@@ -1091,9 +1105,10 @@ exports.examples = [
},
{
title: 'Cache Policy',
description: ('First image will be loaded and will be cached. ' +
'Second image is the same but will be reloaded if re-rendered ' +
'as the cache policy is set to reload.': string),
description: `- First image will be loaded and cached.
- Second image is the same but will be reloaded if re-rendered as the cache policy is set to reload.
- Third image will never be loaded as the cache policy is set to only-if-cached and the image has not been loaded before.
`,
render: function (): React.Node {
return <CacheControlAndroidExample />;
},