From f33a1cd2605309b86201ac9669b95ca87530caec Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Tue, 2 Sep 2025 17:48:39 -0700 Subject: [PATCH] Android: Schedule image prefetching on tree commit (#53555) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53555 Changelog: [Internal] ## TLDR; We run the `ReactVitoImageManager.kt` on the Java Message Queue Thread (`mqt`) > Maybe running it on the `UiThread` (as Android view creation) solves the QE reegressions ## Issue > Your experiment [qe:enable_image_prefetching_android_v4] is significantly moving important metric(s) T235749297 > e.g negatively impact `sp_core` (Scroll Performance Core) https://fburl.com/deltoid3/ef2fd92e {F1981479985} ## Observation After adding Perfetto traces in D80717558 and building a `automation_fbandroid_art_arm64_for_perftest_profileable` build > I see 'larger amounts' of `experimental_prefetchResource` on the JavaScript Message Queue Thread {F1981479808} We do run this entire logic on the JavaScript Message Queue Thread https://www.internalfb.com/code/fbsource/[368503303835439955d87d79439a3d19d979cd40]/fbandroid/java/com/facebook/fresco/vito/rn/ReactVitoImageManager.kt?lines=253-260 However when normally `mounting` Shadow Nodes in RN Android we jump from the JavaScript Message Queue Thread to the Android UI Thread https://www.internalfb.com/code/fbsource/[68603b276cb9de1ae2ecb83ec4a789ae3db3b051]/xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp?lines=626%2C638 -> https://www.internalfb.com/code/fbsource/[68603b276cb9de1ae2ecb83ec4a789ae3db3b051]/xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp?lines=690%2C701%2C872-883 -> https://www.internalfb.com/code/fbsource/[68603b276cb9de1ae2ecb83ec4a789ae3db3b051]/xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java?lines=897%2C943 -> https://www.internalfb.com/code/fbsource/[68603b276cb9de1ae2ecb83ec4a789ae3db3b051]/xplat/js/react-native-github/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java?lines=938-946 ## Idea Run `imagePrefetcher?.prefetchResource` also on the UI thread ## Resources ### :: GDoc - Image Prefetching for Android https://docs.google.com/document/d/1Yc5G5vuollx0I4tdXpE8Hgwn2DuIhJKwOTHak3g4Gu8/edit?fbclid=IwY2xjawLjgcBleHRuA2FlbQIxMQBicmlkETFra3N5WHg3OGV6UndYUmVTAR5KiXIDgrH2FW4HEBdezFBr2NqX4KPT6FzYQXD1sBRjEfq8d_x0JwQfeL_TXg_aem_ZqWb9dAJ59pHFfoHsrzwbw&pli=1&tab=t.0#heading=h.udv4z3lhwhf7 - React Field of View https://docs.google.com/document/d/1gHLF3oAv9JhKKcztM56iZZUPPWp0mBbjqDlosBkL1kE/edit?tab=t.0#heading=h.36p5puf8ufz7 ### :: Fb4A (Facebook for Android) The debug package name for fb4a `com.facebook.katana` is typically `com.facebook.wakizashi` ### :: Links - How to Perfetto profile fb4a https://www.internalfb.com/wiki/Luna_Wei/Building_a_fb4a_Profile_Build/ - Building Catalyst Profile Build https://www.internalfb.com/wiki/Luna_Wei/Building_Catalyst_Profile_Build/ - Marketplace QE Regression Guide https://www.internalfb.com/intern/staticdocs/marketplace/performance/my-experiment-is-regressing-perf/ - Install for Profileable build https://www.internalfb.com/wiki/Metatrace/Metatrace-install_for_Profileable_build/ - Metatrace https://www.internalfb.com/wiki/Metatrace/ ### Android Java Debug https://www.internalfb.com/wiki/Platfrom_Health_Learnings/Onboarding_Material_or_New-hired_Engineers/How_to_Debug_FB4A_0/ ``` arc focus clean --invalidate-caches-only arc focus --targets --open ``` in this case ``` arc focus --targets fb4a --open ``` It creates a `monoproject` now {F1981501090} Reviewed By: javache Differential Revision: D80950423 fbshipit-source-id: 5f1c4c096adab218a2d765d262901521bab2e6b3 --- .../react/fabric/FabricUIManager.java | 10 ++++-- .../mountitems/PrefetchResourcesMountItem.kt | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PrefetchResourcesMountItem.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index 22565058445..a459f15afa2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -65,6 +65,7 @@ import com.facebook.react.fabric.mounting.mountitems.BatchMountItem; import com.facebook.react.fabric.mounting.mountitems.DispatchCommandMountItem; import com.facebook.react.fabric.mounting.mountitems.MountItem; import com.facebook.react.fabric.mounting.mountitems.MountItemFactory; +import com.facebook.react.fabric.mounting.mountitems.PrefetchResourcesMountItem; import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags; import com.facebook.react.internal.interop.InteropEventEmitter; @@ -985,8 +986,13 @@ public class FabricUIManager */ @UnstableReactNativeAPI public void experimental_prefetchResources(String componentName, ReadableMapBuffer params) { - mMountingManager.experimental_prefetchResources( - mReactApplicationContext, componentName, params); + if (ReactNativeFeatureFlags.enableImagePrefetchingOnUiThreadAndroid()) { + mMountItemDispatcher.addMountItem( + new PrefetchResourcesMountItem(mReactApplicationContext, componentName, params)); + } else { + mMountingManager.experimental_prefetchResources( + mReactApplicationContext, componentName, params); + } } void setBinding(FabricUIManagerBinding binding) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PrefetchResourcesMountItem.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PrefetchResourcesMountItem.kt new file mode 100644 index 00000000000..7b1b6e13826 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PrefetchResourcesMountItem.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.fabric.mounting.mountitems + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.common.annotations.FrameworkAPI +import com.facebook.react.common.annotations.UnstableReactNativeAPI +import com.facebook.react.common.mapbuffer.ReadableMapBuffer +import com.facebook.react.fabric.mounting.MountingManager + +internal class PrefetchResourcesMountItem( + private val reactApplicationContext: ReactApplicationContext, + private val componentName: String, + private val params: ReadableMapBuffer, +) : MountItem { + + @OptIn(UnstableReactNativeAPI::class, FrameworkAPI::class) + override fun execute(mountingManager: MountingManager) { + mountingManager.experimental_prefetchResources( + reactApplicationContext, + componentName, + params, + ) + } + + override fun getSurfaceId(): Int = -1 /* unused */ + + override fun toString(): String = "PrefetchResourcesMountItem" +}