Cancel image download when ImageRequest changes (#37223)

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

changelog: [internal]

Cancel image download when image source changes or <ImageView /> is recycled.

bypass-github-export-checks

Reviewed By: javache

Differential Revision: D45524686

fbshipit-source-id: 515a153ddb214fdce51ff29243b68c5fd5479c64
This commit is contained in:
Samuel Susla
2023-05-10 15:52:33 +01:00
committed by Lorenzo Sciandra
parent 9d5ca3647a
commit 166cc09d75
6 changed files with 33 additions and 1 deletions
@@ -14,6 +14,7 @@
#import <react/renderer/components/image/ImageComponentDescriptor.h>
#import <react/renderer/components/image/ImageEventEmitter.h>
#import <react/renderer/components/image/ImageProps.h>
#import <react/renderer/core/CoreFeatures.h>
#import <react/renderer/imagemanager/ImageRequest.h>
#import <react/renderer/imagemanager/RCTImagePrimitivesConversions.h>
@@ -97,8 +98,18 @@ using namespace facebook::react;
- (void)_setStateAndResubscribeImageResponseObserver:(ImageShadowNode::ConcreteState::Shared const &)state
{
if (_state) {
auto &observerCoordinator = _state->getData().getImageRequest().getObserverCoordinator();
auto const &imageRequest = _state->getData().getImageRequest();
auto &observerCoordinator = imageRequest.getObserverCoordinator();
observerCoordinator.removeObserver(_imageResponseObserverProxy);
if (CoreFeatures::cancelImageDownloadsOnRecycle) {
// Cancelling image request because we are no longer observing it.
// This is not 100% correct place to do this because we may want to
// re-create RCTImageComponentView with the same image and if it
// was cancelled before downloaded, download is not resumed.
// This will only become issue if we decouple life cycle of a
// ShadowNode from ComponentView, which is not something we do now.
imageRequest.cancel();
}
}
_state = state;
@@ -285,6 +285,10 @@ static BackgroundExecutor RCTGetBackgroundExecutor()
CoreFeatures::cacheNSTextStorage = true;
}
if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:cancel_image_downloads_on_recycle")) {
CoreFeatures::cancelImageDownloadsOnRecycle = true;
}
auto componentRegistryFactory =
[factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)](
EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) {
@@ -16,6 +16,7 @@ bool CoreFeatures::blockPaintForUseLayoutEffect = false;
bool CoreFeatures::useNativeState = false;
bool CoreFeatures::cacheNSTextStorage = false;
bool CoreFeatures::cacheLastTextMeasurement = false;
bool CoreFeatures::cancelImageDownloadsOnRecycle = false;
} // namespace react
} // namespace facebook
@@ -44,6 +44,10 @@ class CoreFeatures {
// This flag enables a caching mechanism to avoid subsequents measurements
// of the same Text with the same constrainst.
static bool cacheLastTextMeasurement;
// Fabric was not cancelling image downloads when <ImageView /> was removed
// from view hierarchy. This feature flag enables this feature.
static bool cancelImageDownloadsOnRecycle;
};
} // namespace react
@@ -47,6 +47,12 @@ class ImageRequest final {
*/
void setCancelationFunction(std::function<void(void)> cancelationFunction);
/*
* Calls cancel function if one is defined. Should be when downloading
* image isn't needed anymore. E.g. <ImageView /> was removed.
*/
void cancel() const;
/*
* Returns the Image Source associated with the request.
*/
@@ -22,6 +22,12 @@ void ImageRequest::setCancelationFunction(
cancelRequest_ = cancelationFunction;
}
void ImageRequest::cancel() const {
if (cancelRequest_) {
cancelRequest_();
}
}
const ImageSource &ImageRequest::getImageSource() const {
return imageSource_;
}