From beb80c6344f62effe93829298b7ba4838cf8feb9 Mon Sep 17 00:00:00 2001 From: Dmitry Rykun Date: Mon, 25 Nov 2024 10:37:16 -0800 Subject: [PATCH] Android: Initiate image prefetching on ImageShadowNode layout (#47932) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/47932 This diff introduces a code path to trigger image prefetching from `ImageShadowNode::layout`. Changelog: [Internal] Differential Revision: D66454087 --- .../ReactAndroid/api/ReactAndroid.api | 1 + .../react/fabric/FabricUIManager.java | 11 +++++- .../platform/android/ImageFetcher.cpp | 39 +++++++++++++++++++ .../platform/android/ImageFetcher.h | 37 ++++++++++++++++++ .../platform/android/ImageManager.cpp | 34 ++++++++++------ 5 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.cpp create mode 100644 packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.h diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 1445166bc60c44..109fbe18caa4a6 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2638,6 +2638,7 @@ public class com/facebook/react/fabric/FabricUIManager : com/facebook/react/brid public fun receiveEvent (IILjava/lang/String;ZLcom/facebook/react/bridge/WritableMap;IZ)V public fun receiveEvent (ILjava/lang/String;Lcom/facebook/react/bridge/WritableMap;)V public fun removeUIManagerEventListener (Lcom/facebook/react/bridge/UIManagerListener;)V + public fun requestImage (IILcom/facebook/react/common/mapbuffer/ReadableMapBuffer;)V public fun resolveCustomDirectEventName (Ljava/lang/String;)Ljava/lang/String; public fun resolveView (I)Landroid/view/View; public fun sendAccessibilityEvent (II)V 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 983763385ad2a0..565e84bfa338d5 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 @@ -83,6 +83,7 @@ import com.facebook.react.uimanager.events.FabricEventDispatcher; import com.facebook.react.uimanager.events.RCTEventEmitter; import com.facebook.react.uimanager.events.SynchronousEventReceiver; +import com.facebook.react.views.image.ReactImageManager; import com.facebook.react.views.text.TextLayoutManager; import java.util.ArrayList; import java.util.HashMap; @@ -872,6 +873,15 @@ public void runGuarded() { } } + /** + * This method initiates preloading of an image specified by ImageSource. It can later be consumed + * by an ImageView. + */ + public void requestImage(int surfaceId, int reactTag, ReadableMapBuffer params) { + mMountingManager.experimental_prefetchResource( + mReactApplicationContext, ReactImageManager.REACT_CLASS, surfaceId, reactTag, params); + } + public void setBinding(FabricUIManagerBinding binding) { mBinding = binding; } @@ -954,7 +964,6 @@ public void receiveEvent( * @param reactTag * @param eventName * @param canCoalesceEvent - * @param customCoalesceKey * @param params * @param eventCategory */ diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.cpp b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.cpp new file mode 100644 index 00000000000000..4f98314ec8a2c3 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.cpp @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#include "ImageFetcher.h" +#include + +namespace facebook::react { + +ImageFetcher::ImageFetcher(const ContextContainer::Shared& contextContainer) { + fabricUIManager_ = + contextContainer->at>("FabricUIManager"); + requestImage_ = + fabricUIManager_->getClass() + ->getMethod( + "requestImage"); +} + +ImageRequest ImageFetcher::requestImage( + const ImageSource& imageSource, + const ImageRequestParams& imageRequestParams, + SurfaceId surfaceId, + Tag tag) const { + auto serializedImageRequest = + serializeImageRequest(imageSource, imageRequestParams); + + auto readableMapBuffer = + JReadableMapBuffer::createWithContents(std::move(serializedImageRequest)); + + requestImage_(fabricUIManager_, surfaceId, tag, readableMapBuffer.get()); + + auto telemetry = std::make_shared(surfaceId); + + return {imageSource, telemetry}; +} +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.h b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.h new file mode 100644 index 00000000000000..1656b630a87197 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageFetcher.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace facebook::react { + +class ImageFetcher { + public: + ImageFetcher(const ContextContainer::Shared& contextContainer); + + void discardImageRequest(const std::string& imageRequestCacheKey) const; + + ImageRequest requestImage( + const ImageSource& imageSource, + const ImageRequestParams& imageRequestParams, + SurfaceId surfaceId, + Tag tag) const; + + private: + jni::global_ref fabricUIManager_; + jni::JMethod + requestImage_; +}; +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageManager.cpp b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageManager.cpp index 0dd43ae5faded4..54dbb7cf06b129 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/ImageManager.cpp @@ -7,16 +7,20 @@ #include "ImageManager.h" +#include +#include "ImageFetcher.h" + namespace facebook::react { -ImageManager::ImageManager( - const ContextContainer::Shared& /*contextContainer*/) { - // Silence unused-private-field warning. - (void)self_; - // Not implemented. -} +ImageManager::ImageManager(const ContextContainer::Shared& contextContainer) + : self_(new ImageFetcher(contextContainer)) {} -ImageManager::~ImageManager() = default; +ImageManager::~ImageManager() { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcppcoreguidelines-no-malloc" + free(self_); +#pragma clang diagnostic pop +} ImageRequest ImageManager::requestImage( const ImageSource& imageSource, @@ -26,10 +30,18 @@ ImageRequest ImageManager::requestImage( ImageRequest ImageManager::requestImage( const ImageSource& imageSource, - SurfaceId /*surfaceId*/, - const ImageRequestParams& /*imageRequestParams*/, - Tag /* tag */) const { - return {imageSource, nullptr, {}}; + SurfaceId surfaceId, + const ImageRequestParams& imageRequestParams, + Tag tag) const { + if (ReactNativeFeatureFlags::enableImagePrefetchingAndroid()) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcppcoreguidelines-pro-type-cstyle-cast" + return ((ImageFetcher*)self_) + ->requestImage(imageSource, imageRequestParams, surfaceId, tag); +#pragma clang diagnostic pop + } else { + return {imageSource, nullptr, {}}; + } } } // namespace facebook::react