Add Android image prefetching feature flag (#52748)

Summary:
Changelog: [Internal]

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

Reviewed By: rshest

Differential Revision: D78701936

fbshipit-source-id: 878eed13b39ff71487ca47fe7e4ea46459b85ba3
This commit is contained in:
Christoph Purrer
2025-07-22 12:30:57 -07:00
committed by Facebook GitHub Bot
parent ae4ce02752
commit 5e1798ad7a
24 changed files with 187 additions and 68 deletions
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a90efac589511beb130c499e51150de8>>
* @generated SignedSource<<6f50c9b7a356260abf8afc022aba8fc2>>
*/
/**
@@ -162,6 +162,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableIOSViewClipToPaddingBox(): Boolean = accessor.enableIOSViewClipToPaddingBox()
/**
* When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout
*/
@JvmStatic
public fun enableImagePrefetchingAndroid(): Boolean = accessor.enableImagePrefetchingAndroid()
/**
* Dispatches state updates for content offset changes synchronously on the main thread.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<b32f66fb09971e786dd1380bbf417720>>
* @generated SignedSource<<d442e14a9a0f7f5dd2cdbabac34c8415>>
*/
/**
@@ -42,6 +42,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null
private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null
private var enableIOSViewClipToPaddingBoxCache: Boolean? = null
private var enableImagePrefetchingAndroidCache: Boolean? = null
private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null
private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
@@ -281,6 +282,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableImagePrefetchingAndroid(): Boolean {
var cached = enableImagePrefetchingAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableImagePrefetchingAndroid()
enableImagePrefetchingAndroidCache = cached
}
return cached
}
override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean {
var cached = enableImmediateUpdateModeForContentOffsetChangesCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d1da48f826bc6a1793d1630cb89cb5c1>>
* @generated SignedSource<<eb4ba3fd4ce8434f993793febcb8d481>>
*/
/**
@@ -72,6 +72,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableIOSViewClipToPaddingBox(): Boolean
@DoNotStrip @JvmStatic public external fun enableImagePrefetchingAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean
@DoNotStrip @JvmStatic public external fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<c5ed8a904dcda7ade87527233b879b92>>
* @generated SignedSource<<ed1b41e080f0575aff6891aa4fadddce>>
*/
/**
@@ -67,6 +67,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableIOSViewClipToPaddingBox(): Boolean = false
override fun enableImagePrefetchingAndroid(): Boolean = false
override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean = false
override fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean = false
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<23605f090bfbebe911caa9d3d834d3e8>>
* @generated SignedSource<<1c4bee4de12fc3e38a09f2ddf9b39601>>
*/
/**
@@ -46,6 +46,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null
private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null
private var enableIOSViewClipToPaddingBoxCache: Boolean? = null
private var enableImagePrefetchingAndroidCache: Boolean? = null
private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null
private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
@@ -307,6 +308,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun enableImagePrefetchingAndroid(): Boolean {
var cached = enableImagePrefetchingAndroidCache
if (cached == null) {
cached = currentProvider.enableImagePrefetchingAndroid()
accessedFeatureFlags.add("enableImagePrefetchingAndroid")
enableImagePrefetchingAndroidCache = cached
}
return cached
}
override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean {
var cached = enableImmediateUpdateModeForContentOffsetChangesCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<780793412b76f101be1569d7a866c435>>
* @generated SignedSource<<366629baac4727e7db4042a8e9dcab93>>
*/
/**
@@ -67,6 +67,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableIOSViewClipToPaddingBox(): Boolean
@DoNotStrip public fun enableImagePrefetchingAndroid(): Boolean
@DoNotStrip public fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean
@DoNotStrip public fun enableInteropViewManagerClassLookUpOptimizationIOS(): Boolean
@@ -501,6 +501,6 @@ public abstract class ViewManager<T extends View, C extends ReactShadowNode>
@UnstableReactNativeAPI
protected boolean experimental_isPrefetchingEnabled() {
return false;
return ReactNativeFeatureFlags.enableImagePrefetchingAndroid();
}
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f310a5dc27fd655eab4445cb25d4c85c>>
* @generated SignedSource<<1ec5b2dea32e6f318622cb24a9d66ef2>>
*/
/**
@@ -171,6 +171,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool enableImagePrefetchingAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableImagePrefetchingAndroid");
return method(javaProvider_);
}
bool enableImmediateUpdateModeForContentOffsetChanges() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableImmediateUpdateModeForContentOffsetChanges");
@@ -525,6 +531,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableIOSViewClipToPaddingBox(
return ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox();
}
bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableImagePrefetchingAndroid();
}
bool JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges();
@@ -822,6 +833,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableIOSViewClipToPaddingBox",
JReactNativeFeatureFlagsCxxInterop::enableIOSViewClipToPaddingBox),
makeNativeMethod(
"enableImagePrefetchingAndroid",
JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid),
makeNativeMethod(
"enableImmediateUpdateModeForContentOffsetChanges",
JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges),
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<8c1da07c0b7d2053f7fdaac4326c3ac1>>
* @generated SignedSource<<3570d50c3ff20d976b7145c0c36fd6ac>>
*/
/**
@@ -96,6 +96,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableIOSViewClipToPaddingBox(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableImagePrefetchingAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableImmediateUpdateModeForContentOffsetChanges(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<355265aa8f13e3f307d6e30db6b80d41>>
* @generated SignedSource<<1c8a4501d6e0d7d0b162283cbbdf6a87>>
*/
/**
@@ -114,6 +114,10 @@ bool ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox() {
return getAccessor().enableIOSViewClipToPaddingBox();
}
bool ReactNativeFeatureFlags::enableImagePrefetchingAndroid() {
return getAccessor().enableImagePrefetchingAndroid();
}
bool ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges() {
return getAccessor().enableImmediateUpdateModeForContentOffsetChanges();
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<c584290ce61ee78bdf843c19dd40c40d>>
* @generated SignedSource<<3edca00b84d9b4d30c7d9bf5a8dee039>>
*/
/**
@@ -149,6 +149,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableIOSViewClipToPaddingBox();
/**
* When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout
*/
RN_EXPORT static bool enableImagePrefetchingAndroid();
/**
* Dispatches state updates for content offset changes synchronously on the main thread.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<dc5c6b3273ef312426c4ac366cc13772>>
* @generated SignedSource<<5149f256e2692114e5d4b4efb839589a>>
*/
/**
@@ -425,6 +425,24 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() {
auto flagValue = enableImagePrefetchingAndroid_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(22, "enableImagePrefetchingAndroid");
flagValue = currentProvider_->enableImagePrefetchingAndroid();
enableImagePrefetchingAndroid_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetChanges() {
auto flagValue = enableImmediateUpdateModeForContentOffsetChanges_.load();
@@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetC
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(22, "enableImmediateUpdateModeForContentOffsetChanges");
markFlagAsAccessed(23, "enableImmediateUpdateModeForContentOffsetChanges");
flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges();
enableImmediateUpdateModeForContentOffsetChanges_ = flagValue;
@@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(23, "enableInteropViewManagerClassLookUpOptimizationIOS");
markFlagAsAccessed(24, "enableInteropViewManagerClassLookUpOptimizationIOS");
flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS();
enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue;
@@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(24, "enableLayoutAnimationsOnAndroid");
markFlagAsAccessed(25, "enableLayoutAnimationsOnAndroid");
flagValue = currentProvider_->enableLayoutAnimationsOnAndroid();
enableLayoutAnimationsOnAndroid_ = flagValue;
@@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(25, "enableLayoutAnimationsOnIOS");
markFlagAsAccessed(26, "enableLayoutAnimationsOnIOS");
flagValue = currentProvider_->enableLayoutAnimationsOnIOS();
enableLayoutAnimationsOnIOS_ = flagValue;
@@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueCoordinatorOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(26, "enableMainQueueCoordinatorOnIOS");
markFlagAsAccessed(27, "enableMainQueueCoordinatorOnIOS");
flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS();
enableMainQueueCoordinatorOnIOS_ = flagValue;
@@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(27, "enableModuleArgumentNSNullConversionIOS");
markFlagAsAccessed(28, "enableModuleArgumentNSNullConversionIOS");
flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS();
enableModuleArgumentNSNullConversionIOS_ = flagValue;
@@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(28, "enableNativeCSSParsing");
markFlagAsAccessed(29, "enableNativeCSSParsing");
flagValue = currentProvider_->enableNativeCSSParsing();
enableNativeCSSParsing_ = flagValue;
@@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(29, "enableNetworkEventReporting");
markFlagAsAccessed(30, "enableNetworkEventReporting");
flagValue = currentProvider_->enableNetworkEventReporting();
enableNetworkEventReporting_ = flagValue;
@@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNewBackgroundAndBorderDrawables() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(30, "enableNewBackgroundAndBorderDrawables");
markFlagAsAccessed(31, "enableNewBackgroundAndBorderDrawables");
flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables();
enableNewBackgroundAndBorderDrawables_ = flagValue;
@@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(31, "enablePreparedTextLayout");
markFlagAsAccessed(32, "enablePreparedTextLayout");
flagValue = currentProvider_->enablePreparedTextLayout();
enablePreparedTextLayout_ = flagValue;
@@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(32, "enablePropsUpdateReconciliationAndroid");
markFlagAsAccessed(33, "enablePropsUpdateReconciliationAndroid");
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
enablePropsUpdateReconciliationAndroid_ = flagValue;
@@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::enableResourceTimingAPI() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(33, "enableResourceTimingAPI");
markFlagAsAccessed(34, "enableResourceTimingAPI");
flagValue = currentProvider_->enableResourceTimingAPI();
enableResourceTimingAPI_ = flagValue;
@@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(34, "enableViewCulling");
markFlagAsAccessed(35, "enableViewCulling");
flagValue = currentProvider_->enableViewCulling();
enableViewCulling_ = flagValue;
@@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(35, "enableViewRecycling");
markFlagAsAccessed(36, "enableViewRecycling");
flagValue = currentProvider_->enableViewRecycling();
enableViewRecycling_ = flagValue;
@@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(36, "enableViewRecyclingForText");
markFlagAsAccessed(37, "enableViewRecyclingForText");
flagValue = currentProvider_->enableViewRecyclingForText();
enableViewRecyclingForText_ = flagValue;
@@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(37, "enableViewRecyclingForView");
markFlagAsAccessed(38, "enableViewRecyclingForView");
flagValue = currentProvider_->enableViewRecyclingForView();
enableViewRecyclingForView_ = flagValue;
@@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(38, "enableVirtualViewDebugFeatures");
markFlagAsAccessed(39, "enableVirtualViewDebugFeatures");
flagValue = currentProvider_->enableVirtualViewDebugFeatures();
enableVirtualViewDebugFeatures_ = flagValue;
@@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(39, "enableVirtualViewRenderState");
markFlagAsAccessed(40, "enableVirtualViewRenderState");
flagValue = currentProvider_->enableVirtualViewRenderState();
enableVirtualViewRenderState_ = flagValue;
@@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(40, "enableVirtualViewWindowFocusDetection");
markFlagAsAccessed(41, "enableVirtualViewWindowFocusDetection");
flagValue = currentProvider_->enableVirtualViewWindowFocusDetection();
enableVirtualViewWindowFocusDetection_ = flagValue;
@@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(41, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
markFlagAsAccessed(42, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(42, "fuseboxEnabledRelease");
markFlagAsAccessed(43, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(43, "fuseboxNetworkInspectionEnabled");
markFlagAsAccessed(44, "fuseboxNetworkInspectionEnabled");
flagValue = currentProvider_->fuseboxNetworkInspectionEnabled();
fuseboxNetworkInspectionEnabled_ = flagValue;
@@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(44, "hideOffscreenVirtualViewsOnIOS");
markFlagAsAccessed(45, "hideOffscreenVirtualViewsOnIOS");
flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS();
hideOffscreenVirtualViewsOnIOS_ = flagValue;
@@ -848,7 +866,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(45, "preparedTextCacheSize");
markFlagAsAccessed(46, "preparedTextCacheSize");
flagValue = currentProvider_->preparedTextCacheSize();
preparedTextCacheSize_ = flagValue;
@@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustionWithLocki
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(46, "preventShadowTreeCommitExhaustionWithLocking");
markFlagAsAccessed(47, "preventShadowTreeCommitExhaustionWithLocking");
flagValue = currentProvider_->preventShadowTreeCommitExhaustionWithLocking();
preventShadowTreeCommitExhaustionWithLocking_ = flagValue;
@@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(47, "releaseImageDataWhenConsumed");
markFlagAsAccessed(48, "releaseImageDataWhenConsumed");
flagValue = currentProvider_->releaseImageDataWhenConsumed();
releaseImageDataWhenConsumed_ = flagValue;
@@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(48, "skipActivityIdentityAssertionOnHostPause");
markFlagAsAccessed(49, "skipActivityIdentityAssertionOnHostPause");
flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause();
skipActivityIdentityAssertionOnHostPause_ = flagValue;
@@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(49, "traceTurboModulePromiseRejectionsOnAndroid");
markFlagAsAccessed(50, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit(
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(50, "updateRuntimeShadowNodeReferencesOnCommit");
markFlagAsAccessed(51, "updateRuntimeShadowNodeReferencesOnCommit");
flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit();
updateRuntimeShadowNodeReferencesOnCommit_ = flagValue;
@@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(51, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(52, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(52, "useFabricInterop");
markFlagAsAccessed(53, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(53, "useNativeEqualsInNativeReadableArrayAndroid");
markFlagAsAccessed(54, "useNativeEqualsInNativeReadableArrayAndroid");
flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid();
useNativeEqualsInNativeReadableArrayAndroid_ = flagValue;
@@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(54, "useNativeTransformHelperAndroid");
markFlagAsAccessed(55, "useNativeTransformHelperAndroid");
flagValue = currentProvider_->useNativeTransformHelperAndroid();
useNativeTransformHelperAndroid_ = flagValue;
@@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(55, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(56, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(56, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(57, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(57, "useRawPropsJsiValue");
markFlagAsAccessed(58, "useRawPropsJsiValue");
flagValue = currentProvider_->useRawPropsJsiValue();
useRawPropsJsiValue_ = flagValue;
@@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(58, "useShadowNodeStateOnClone");
markFlagAsAccessed(59, "useShadowNodeStateOnClone");
flagValue = currentProvider_->useShadowNodeStateOnClone();
useShadowNodeStateOnClone_ = flagValue;
@@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(59, "useTurboModuleInterop");
markFlagAsAccessed(60, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(60, "useTurboModules");
markFlagAsAccessed(61, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;
@@ -1136,7 +1154,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(61, "virtualViewPrerenderRatio");
markFlagAsAccessed(62, "virtualViewPrerenderRatio");
flagValue = currentProvider_->virtualViewPrerenderRatio();
virtualViewPrerenderRatio_ = flagValue;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<edfd880ca5034a683053dc95e177a7c2>>
* @generated SignedSource<<f9c9fbadc2d5324efc94a17705fc6e9a>>
*/
/**
@@ -54,6 +54,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableFontScaleChangesUpdatingLayout();
bool enableIOSTextBaselineOffsetPerLine();
bool enableIOSViewClipToPaddingBox();
bool enableImagePrefetchingAndroid();
bool enableImmediateUpdateModeForContentOffsetChanges();
bool enableInteropViewManagerClassLookUpOptimizationIOS();
bool enableLayoutAnimationsOnAndroid();
@@ -105,7 +106,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 62> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 63> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> cxxNativeAnimatedEnabled_;
@@ -129,6 +130,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableFontScaleChangesUpdatingLayout_;
std::atomic<std::optional<bool>> enableIOSTextBaselineOffsetPerLine_;
std::atomic<std::optional<bool>> enableIOSViewClipToPaddingBox_;
std::atomic<std::optional<bool>> enableImagePrefetchingAndroid_;
std::atomic<std::optional<bool>> enableImmediateUpdateModeForContentOffsetChanges_;
std::atomic<std::optional<bool>> enableInteropViewManagerClassLookUpOptimizationIOS_;
std::atomic<std::optional<bool>> enableLayoutAnimationsOnAndroid_;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<614fe0894f6235d879a0b29e7dab7759>>
* @generated SignedSource<<bd80e3802bf97fabf66e24db211bf1f4>>
*/
/**
@@ -115,6 +115,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool enableImagePrefetchingAndroid() override {
return false;
}
bool enableImmediateUpdateModeForContentOffsetChanges() override {
return false;
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<6502c090532e1d7d2ac67cf6e711d9f4>>
* @generated SignedSource<<44670421b1b5991cd6af3fb7b1c1bf6c>>
*/
/**
@@ -243,6 +243,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef
return ReactNativeFeatureFlagsDefaults::enableIOSViewClipToPaddingBox();
}
bool enableImagePrefetchingAndroid() override {
auto value = values_["enableImagePrefetchingAndroid"];
if (!value.isNull()) {
return value.getBool();
}
return ReactNativeFeatureFlagsDefaults::enableImagePrefetchingAndroid();
}
bool enableImmediateUpdateModeForContentOffsetChanges() override {
auto value = values_["enableImmediateUpdateModeForContentOffsetChanges"];
if (!value.isNull()) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a26af781141939d35cff83bacf522bb0>>
* @generated SignedSource<<082c6d17eec877d054193eb9dea45b6d>>
*/
/**
@@ -47,6 +47,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableFontScaleChangesUpdatingLayout() = 0;
virtual bool enableIOSTextBaselineOffsetPerLine() = 0;
virtual bool enableIOSViewClipToPaddingBox() = 0;
virtual bool enableImagePrefetchingAndroid() = 0;
virtual bool enableImmediateUpdateModeForContentOffsetChanges() = 0;
virtual bool enableInteropViewManagerClassLookUpOptimizationIOS() = 0;
virtual bool enableLayoutAnimationsOnAndroid() = 0;
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cadb1f2c1c10a03593dc7c9a9a94747b>>
* @generated SignedSource<<5b33805daf165bd61f382354685d1d70>>
*/
/**
@@ -154,6 +154,11 @@ bool NativeReactNativeFeatureFlags::enableIOSViewClipToPaddingBox(
return ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox();
}
bool NativeReactNativeFeatureFlags::enableImagePrefetchingAndroid(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableImagePrefetchingAndroid();
}
bool NativeReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges();
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<de04d72490f2c09abdcea51e6f02796c>>
* @generated SignedSource<<3487c7ee26cfd5776c85dcf6a6e7fc10>>
*/
/**
@@ -80,6 +80,8 @@ class NativeReactNativeFeatureFlags
bool enableIOSViewClipToPaddingBox(jsi::Runtime& runtime);
bool enableImagePrefetchingAndroid(jsi::Runtime& runtime);
bool enableImmediateUpdateModeForContentOffsetChanges(jsi::Runtime& runtime);
bool enableInteropViewManagerClassLookUpOptimizationIOS(jsi::Runtime& runtime);
@@ -6,7 +6,10 @@
*/
#include "ImageFetcher.h"
#include <react/common/mapbuffer/JReadableMapBuffer.h>
#include <react/renderer/imagemanager/conversions.h>
#include <utility>
namespace facebook::react {
@@ -40,4 +43,5 @@ ImageRequest ImageFetcher::requestImage(
return {imageSource, telemetry};
}
} // namespace facebook::react
@@ -7,16 +7,10 @@
#pragma once
#include <fbjni/fbjni.h>
#include <react/common/mapbuffer/JReadableMapBuffer.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/imagemanager/ImageRequest.h>
#include <react/renderer/imagemanager/ImageRequestParams.h>
#include <react/utils/ContextContainer.h>
#include <utility>
namespace facebook::react {
class ImageFetcher {
@@ -6,9 +6,9 @@
*/
#include "ImageManager.h"
#include "ImageFetcher.h"
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include "ImageFetcher.h"
namespace facebook::react {
@@ -30,6 +30,10 @@ ImageRequest ImageManager::requestImage(
SurfaceId surfaceId,
const ImageRequestParams& imageRequestParams,
Tag tag) const {
if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) {
return static_cast<ImageFetcher*>(self_)->requestImage(
imageSource, imageRequestParams, surfaceId, tag);
}
return {imageSource, nullptr, {}};
}
@@ -281,6 +281,17 @@ const definitions: FeatureFlagDefinitions = {
},
ossReleaseStage: 'none',
},
enableImagePrefetchingAndroid: {
defaultValue: false,
metadata: {
dateAdded: '2025-06-21',
description:
'When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout',
expectedReleaseValue: true,
purpose: 'experimentation',
},
ossReleaseStage: 'none',
},
enableImmediateUpdateModeForContentOffsetChanges: {
defaultValue: false,
metadata: {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a5b97c3df370225b7601f7375bb03ce0>>
* @generated SignedSource<<d06db4564bc92829c8191ecbddeb3829>>
* @flow strict
* @noformat
*/
@@ -68,6 +68,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{
enableFontScaleChangesUpdatingLayout: Getter<boolean>,
enableIOSTextBaselineOffsetPerLine: Getter<boolean>,
enableIOSViewClipToPaddingBox: Getter<boolean>,
enableImagePrefetchingAndroid: Getter<boolean>,
enableImmediateUpdateModeForContentOffsetChanges: Getter<boolean>,
enableInteropViewManagerClassLookUpOptimizationIOS: Getter<boolean>,
enableLayoutAnimationsOnAndroid: Getter<boolean>,
@@ -257,6 +258,10 @@ export const enableIOSTextBaselineOffsetPerLine: Getter<boolean> = createNativeF
* iOS Views will clip to their padding box vs border box
*/
export const enableIOSViewClipToPaddingBox: Getter<boolean> = createNativeFlagGetter('enableIOSViewClipToPaddingBox', false);
/**
* When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout
*/
export const enableImagePrefetchingAndroid: Getter<boolean> = createNativeFlagGetter('enableImagePrefetchingAndroid', false);
/**
* Dispatches state updates for content offset changes synchronously on the main thread.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<45c98e5f4b5f3d3a8a0ded4c5148b549>>
* @generated SignedSource<<8051b5c10479f9ad92faad99586578d9>>
* @flow strict
* @noformat
*/
@@ -47,6 +47,7 @@ export interface Spec extends TurboModule {
+enableFontScaleChangesUpdatingLayout?: () => boolean;
+enableIOSTextBaselineOffsetPerLine?: () => boolean;
+enableIOSViewClipToPaddingBox?: () => boolean;
+enableImagePrefetchingAndroid?: () => boolean;
+enableImmediateUpdateModeForContentOffsetChanges?: () => boolean;
+enableInteropViewManagerClassLookUpOptimizationIOS?: () => boolean;
+enableLayoutAnimationsOnAndroid?: () => boolean;