mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Replace RCTLocalAssetImageLoader to RCTBundleAssetImageLoader (#37232)
Summary: From the video below, we can see that the UI thread has dropped many frames, and it would become worse if there are multiple images. If an image is located in the sandbox of the disk, we cannot load it using `RCTLocalAssetImageLoader` because `RCTLocalAssetImageLoader.requiresScheduling` is set to true, which loads the data on the UI thread and causes main thread stuttering. This will affect libraries such as `react-native-code-push` and others that save images to the sandbox of the disk. Therefore, we should replace `RCTLocalAssetImageLoader.canLoadImageURL` from `RCTIsLocalAssetURL(url)` to `RCTIsBundleAssetURL(url)`. Similarly, we should rename the entire `RCTLocalAssetImageLoader` file with `RCTBundleAssetImageLoader`, which ignores images in the disk sandbox. And finally these images will be loaded from `NSURLRequest`, and our UI thread will run smoothly again. https://user-images.githubusercontent.com/20135674/236368418-8933a2c6-549c-40d3-a551-81b492fe41d5.mp4 ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [IOS] [Breaking] - Replace `RCTLocalAssetImageLoader` to `RCTBundleAssetImageLoader` Pull Request resolved: https://github.com/facebook/react-native/pull/37232 Test Plan: Test Code: ```javascript constructor(props) { super(props) this.state = { bundle_image: require('./large_image.png'), sandbox_image: '', source: null, isLoading: false, } } render() { console.log('render', this.state) return ( <View style={{ flex: 1, padding: 50, backgroundColor: 'white'}}> <View style={{ flexDirection: 'row', alignItems: 'center', height: 70}}> { [{ title: 'Save Image To SandBox', onPress: () => { let image = Image.resolveAssetSource(this.state.bundle_image) console.log(image.uri) this.setState({ isLoading: true }) RNFetchBlob.config({ fileCache: true, appendExt: "png" }).fetch("GET", image.uri).then(response => { let path = response.path() path = /^file:\/\//.test(path) ? path : 'file://' + path console.log(path) this.state.sandbox_image = path }).finally(() => this.setState({ isLoading: false })) }}, { title: 'Load From SandBox', onPress: () => { this.setState({ source: { uri: this.state.sandbox_image } }) }}, { title: 'Clear', onPress: () => { this.setState({ source: null, isLoading: false }) }}, { title: 'Load From Bundle', onPress: () => { this.setState({ source: this.state.bundle_image }) }}].map((item, index) => { return ( <Pressable key={index} style={{ height: '100%', justifyContent: 'center', flex: 1, borderWidth: 1, borderColor: 'black', marginLeft: index > 0 ? 15 : 0 }} onPress={item.onPress} > <Text style={{ textAlign: 'center' }}>{item.title}</Text> </Pressable> ) }) } </View> <ActivityIndicator style={{ marginTop: 10 }} animating={this.state.isLoading} /> <Image key={`${this.state.source}`} style={{ marginTop: 20, width: 200, height: 200 }} source={this.state.source} onProgress={({ nativeEvent }) => console.log(nativeEvent)} onLoadStart={() => this.setState({ isLoading: true })} onLoadEnd={() => this.setState({ isLoading: false })} /> </View> ) } ``` It needs to be tested in three environments: [Simulator_Debug, RealDevice_Debug, RealDevice_Release] 1. Open `Perf Monitor` (RealDevice_Release can be skipped) 2. Click `Save Image to SandBox` 3. Wait for the loading to end and click `Load From SandBox` 4. Verify that the image can be loaded successfully 5. Verify that the `UI thread` keeps `60 FPS` (RealDevice_Release can be skipped) 6. Click `Clear` 7. Repeat steps [3, 4, 5, 6] several times 8. Click `Load From Bundle` to verify that the bundle image can be loaded successfully Simulator_Debug https://user-images.githubusercontent.com/20135674/236369344-ee1b8ff1-2d49-49f3-a322-d973f4adf3e7.mp4 RealDevice_Debug https://user-images.githubusercontent.com/20135674/236369356-fe440b2b-f72a-49be-b63c-b4bf709dac8c.mp4 RealDevice_Release https://user-images.githubusercontent.com/20135674/236369365-8a6a5c2f-09ad-4c90-b6bd-41e8a5e3aa7f.mp4 Reviewed By: rshest Differential Revision: D46441513 Pulled By: dmytrorykun fbshipit-source-id: 652febd4147dbff6c1ceef03d84ce125b8c66770
This commit is contained in:
committed by
Facebook GitHub Bot
parent
f6c417c7ae
commit
b675667a47
@@ -10,13 +10,13 @@
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
// Turbo Module
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
#import <React/RCTBundleAssetImageLoader.h>
|
||||
#import <React/RCTDataRequestHandler.h>
|
||||
#import <React/RCTFileRequestHandler.h>
|
||||
#import <React/RCTGIFImageDecoder.h>
|
||||
#import <React/RCTHTTPRequestHandler.h>
|
||||
#import <React/RCTImageLoader.h>
|
||||
#import <React/RCTJSIExecutorRuntimeInstaller.h>
|
||||
#import <React/RCTLocalAssetImageLoader.h>
|
||||
#import <React/RCTNetworking.h>
|
||||
|
||||
// Fabric
|
||||
@@ -83,7 +83,7 @@ id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass)
|
||||
if (moduleClass == RCTImageLoader.class) {
|
||||
return [[moduleClass alloc] initWithRedirectDelegate:nil
|
||||
loadersProvider:^NSArray<id<RCTImageURLLoader>> *(RCTModuleRegistry *moduleRegistry) {
|
||||
return @[ [RCTLocalAssetImageLoader new] ];
|
||||
return @[ [RCTBundleAssetImageLoader new] ];
|
||||
}
|
||||
decodersProvider:^NSArray<id<RCTImageDataDecoder>> *(RCTModuleRegistry *moduleRegistry) {
|
||||
return @[ [RCTGIFImageDecoder new] ];
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <React/RCTImageURLLoader.h>
|
||||
|
||||
@interface RCTBundleAssetImageLoader : NSObject <RCTImageURLLoader>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#import <React/RCTBundleAssetImageLoader.h>
|
||||
|
||||
#import <atomic>
|
||||
#import <memory>
|
||||
|
||||
#import <React/RCTUtils.h>
|
||||
#import <ReactCommon/RCTTurboModule.h>
|
||||
|
||||
#import "RCTImagePlugins.h"
|
||||
|
||||
@interface RCTBundleAssetImageLoader () <RCTTurboModule>
|
||||
@end
|
||||
|
||||
@implementation RCTBundleAssetImageLoader
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (BOOL)canLoadImageURL:(NSURL *)requestURL
|
||||
{
|
||||
return RCTIsBundleAssetURL(requestURL);
|
||||
}
|
||||
|
||||
- (BOOL)requiresScheduling
|
||||
{
|
||||
// Don't schedule this loader on the URL queue so we can load the
|
||||
// local assets synchronously to avoid flickers.
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)shouldCacheLoadedImages
|
||||
{
|
||||
// UIImage imageNamed handles the caching automatically so we don't want
|
||||
// to add it to the image cache.
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (nullable RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
|
||||
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
|
||||
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||
{
|
||||
UIImage *image = RCTImageFromLocalAssetURL(imageURL);
|
||||
if (image) {
|
||||
if (progressHandler) {
|
||||
progressHandler(1, 1);
|
||||
}
|
||||
completionHandler(nil, image);
|
||||
} else {
|
||||
NSString *message = [NSString stringWithFormat:@"Could not find image %@", imageURL];
|
||||
RCTLogWarn(@"%@", message);
|
||||
completionHandler(RCTErrorWithMessage(message), nil);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
||||
(const facebook::react::ObjCTurboModule::InitParams &)params
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (float)loaderPriority
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class RCTBundleAssetImageLoaderCls(void)
|
||||
{
|
||||
return RCTBundleAssetImageLoader.class;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#import <React/RCTImageURLLoader.h>
|
||||
|
||||
@interface RCTLocalAssetImageLoader : NSObject <RCTImageURLLoader>
|
||||
__deprecated_msg("Use RCTBundleAssetImageLoader instead") @interface RCTLocalAssetImageLoader
|
||||
: NSObject<RCTImageURLLoader>
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,7 +24,7 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
- (BOOL)canLoadImageURL:(NSURL *)requestURL
|
||||
{
|
||||
return RCTIsLocalAssetURL(requestURL);
|
||||
return RCTIsBundleAssetURL(requestURL);
|
||||
}
|
||||
|
||||
- (BOOL)requiresScheduling
|
||||
@@ -70,6 +70,11 @@ RCT_EXPORT_MODULE()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
- (float)loaderPriority
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Class RCTLocalAssetImageLoaderCls(void)
|
||||
|
||||
Reference in New Issue
Block a user