mirror of
https://github.com/status-im/react-native.git
synced 2025-01-27 01:40:08 +00:00
Reduced work done on main thread by RCTImageLoader
Summary: public Removed redundant calls to [RCTNetwork canHandleRequest] in release mode when loading images, and improved perf for handler lookups when running in debug mode. Reviewed By: tadeuzagallo Differential Revision: D2663307 fb-gh-sync-id: 13285154c1c3773b32dba7894d86d14992e2fd7d
This commit is contained in:
parent
c043c68e7e
commit
5b796cec34
@ -99,8 +99,11 @@ RCT_EXPORT_MODULE()
|
||||
float previousPriority = 0;
|
||||
id<RCTImageURLLoader> previousLoader = nil;
|
||||
for (id<RCTImageURLLoader> loader in _loaders) {
|
||||
float priority = [loader respondsToSelector:@selector(loaderPriority)] ? [loader loaderPriority] : 0;
|
||||
if (previousLoader && priority < previousPriority) {
|
||||
return previousLoader;
|
||||
}
|
||||
if ([loader canLoadImageURL:URL]) {
|
||||
float priority = [loader respondsToSelector:@selector(loaderPriority)] ? [loader loaderPriority] : 0;
|
||||
if (previousLoader) {
|
||||
if (priority == previousPriority) {
|
||||
RCTLogError(@"The RCTImageURLLoaders %@ and %@ both reported that"
|
||||
@ -114,6 +117,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
}
|
||||
}
|
||||
return previousLoader;
|
||||
}
|
||||
|
||||
// Normal code path
|
||||
@ -132,8 +136,11 @@ RCT_EXPORT_MODULE()
|
||||
float previousPriority = 0;
|
||||
id<RCTImageDataDecoder> previousDecoder = nil;
|
||||
for (id<RCTImageDataDecoder> decoder in _decoders) {
|
||||
float priority = [decoder respondsToSelector:@selector(decoderPriority)] ? [decoder decoderPriority] : 0;
|
||||
if (previousDecoder && priority < previousPriority) {
|
||||
return previousDecoder;
|
||||
}
|
||||
if ([decoder canDecodeImageData:data]) {
|
||||
float priority = [decoder respondsToSelector:@selector(decoderPriority)] ? [decoder decoderPriority] : 0;
|
||||
if (previousDecoder) {
|
||||
if (priority == previousPriority) {
|
||||
RCTLogError(@"The RCTImageDataDecoders %@ and %@ both reported that"
|
||||
@ -148,6 +155,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
}
|
||||
}
|
||||
return previousDecoder;
|
||||
}
|
||||
|
||||
// Normal code path
|
||||
@ -211,101 +219,101 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
|
||||
// Check if networking module is available
|
||||
if (![_bridge respondsToSelector:@selector(networking)]) {
|
||||
if (RCT_DEBUG && ![_bridge respondsToSelector:@selector(networking)]) {
|
||||
RCTLogError(@"No suitable image URL loader found for %@. You may need to "
|
||||
" import the RCTNetworking library in order to load images.",
|
||||
imageTag);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
// Use networking module to load image
|
||||
if ([_bridge.networking canHandleRequest:request]) {
|
||||
|
||||
__weak RCTImageLoader *weakSelf = self;
|
||||
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
|
||||
RCTURLRequestCompletionBlock processResponse =
|
||||
^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
|
||||
// Check for system errors
|
||||
if (error) {
|
||||
completionHandler(error, nil);
|
||||
return;
|
||||
} else if (!data) {
|
||||
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for http errors
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
||||
if (statusCode != 200) {
|
||||
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||
code:statusCode
|
||||
userInfo:nil], nil);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode image
|
||||
decodeCancel = [weakSelf decodeImageData:data
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
completionBlock:completionHandler];
|
||||
};
|
||||
|
||||
// Check for cached response before reloading
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
|
||||
if (cachedResponse) {
|
||||
processResponse(cachedResponse.response, cachedResponse.data, nil);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
// Add missing png extension
|
||||
if (request.URL.fileURL && request.URL.pathExtension.length == 0) {
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]];
|
||||
request = mutableRequest;
|
||||
}
|
||||
|
||||
// Download image
|
||||
RCTNetworkTask *task = [_bridge.networking networkTaskWithRequest:request completionBlock:
|
||||
^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
if (error) {
|
||||
completionHandler(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the response
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
RCTImageLoader *strongSelf = weakSelf;
|
||||
BOOL isHTTPRequest = [request.URL.scheme hasPrefix:@"http"];
|
||||
[strongSelf->_cache storeCachedResponse:
|
||||
[[NSCachedURLResponse alloc] initWithResponse:response
|
||||
data:data
|
||||
userInfo:nil
|
||||
storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly]
|
||||
forRequest:request];
|
||||
|
||||
// Process image data
|
||||
processResponse(response, data, nil);
|
||||
|
||||
}];
|
||||
task.downloadProgressBlock = progressHandler;
|
||||
[task start];
|
||||
|
||||
return ^{
|
||||
[task cancel];
|
||||
if (decodeCancel) {
|
||||
decodeCancel();
|
||||
}
|
||||
OSAtomicOr32Barrier(1, &cancelled);
|
||||
};
|
||||
// Check if networking module can load image
|
||||
if (RCT_DEBUG && ![_bridge.networking canHandleRequest:request]) {
|
||||
RCTLogError(@"No suitable image URL loader found for %@", imageTag);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
RCTLogError(@"No suitable image URL loader found for %@", imageTag);
|
||||
return ^{};
|
||||
// Use networking module to load image
|
||||
__weak RCTImageLoader *weakSelf = self;
|
||||
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
|
||||
RCTURLRequestCompletionBlock processResponse =
|
||||
^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
|
||||
// Check for system errors
|
||||
if (error) {
|
||||
completionHandler(error, nil);
|
||||
return;
|
||||
} else if (!data) {
|
||||
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for http errors
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
||||
if (statusCode != 200) {
|
||||
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||
code:statusCode
|
||||
userInfo:nil], nil);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode image
|
||||
decodeCancel = [weakSelf decodeImageData:data
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
completionBlock:completionHandler];
|
||||
};
|
||||
|
||||
// Check for cached response before reloading
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
|
||||
if (cachedResponse) {
|
||||
processResponse(cachedResponse.response, cachedResponse.data, nil);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
// Add missing png extension
|
||||
if (request.URL.fileURL && request.URL.pathExtension.length == 0) {
|
||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||
mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]];
|
||||
request = mutableRequest;
|
||||
}
|
||||
|
||||
// Download image
|
||||
RCTNetworkTask *task = [_bridge.networking networkTaskWithRequest:request completionBlock:
|
||||
^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
if (error) {
|
||||
completionHandler(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the response
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
RCTImageLoader *strongSelf = weakSelf;
|
||||
BOOL isHTTPRequest = [request.URL.scheme hasPrefix:@"http"];
|
||||
[strongSelf->_cache storeCachedResponse:
|
||||
[[NSCachedURLResponse alloc] initWithResponse:response
|
||||
data:data
|
||||
userInfo:nil
|
||||
storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly]
|
||||
forRequest:request];
|
||||
|
||||
// Process image data
|
||||
processResponse(response, data, nil);
|
||||
|
||||
}];
|
||||
task.downloadProgressBlock = progressHandler;
|
||||
[task start];
|
||||
|
||||
return ^{
|
||||
[task cancel];
|
||||
if (decodeCancel) {
|
||||
decodeCancel();
|
||||
}
|
||||
OSAtomicOr32Barrier(1, &cancelled);
|
||||
};
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
|
||||
@ -314,6 +322,11 @@ RCT_EXPORT_MODULE()
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completionHandler
|
||||
{
|
||||
if (data.length == 0) {
|
||||
completionHandler(RCTErrorWithMessage(@"No image data"), nil);
|
||||
return ^{};
|
||||
}
|
||||
|
||||
id<RCTImageDataDecoder> imageDecoder = [self imageDataDecoderForData:data];
|
||||
if (imageDecoder) {
|
||||
|
||||
|
@ -183,6 +183,19 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
resizeMode:self.contentMode
|
||||
progressBlock:progressHandler
|
||||
completionBlock:^(NSError *error, UIImage *image) {
|
||||
if (error) {
|
||||
if (_onError) {
|
||||
_onError(@{ @"error": error.localizedDescription });
|
||||
}
|
||||
} else {
|
||||
if (_onLoad) {
|
||||
_onLoad(nil);
|
||||
}
|
||||
}
|
||||
if (_onLoadEnd) {
|
||||
_onLoadEnd(nil);
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (image.reactKeyframeAnimation) {
|
||||
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
|
||||
@ -190,18 +203,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.image = image;
|
||||
}
|
||||
if (error) {
|
||||
if (_onError) {
|
||||
_onError(@{ @"error": error.localizedDescription });
|
||||
}
|
||||
} else {
|
||||
if (_onLoad) {
|
||||
_onLoad(nil);
|
||||
}
|
||||
}
|
||||
if (_onLoadEnd) {
|
||||
_onLoadEnd(nil);
|
||||
}
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
|
@ -165,8 +165,11 @@ RCT_EXPORT_MODULE()
|
||||
float previousPriority = 0;
|
||||
id<RCTURLRequestHandler> previousHandler = nil;
|
||||
for (id<RCTURLRequestHandler> handler in _handlers) {
|
||||
float priority = [handler respondsToSelector:@selector(handlerPriority)] ? [handler handlerPriority] : 0;
|
||||
if (previousHandler && priority < previousPriority) {
|
||||
return previousHandler;
|
||||
}
|
||||
if ([handler canHandleRequest:request]) {
|
||||
float priority = [handler respondsToSelector:@selector(handlerPriority)] ? [handler handlerPriority] : 0;
|
||||
if (previousHandler) {
|
||||
if (priority == previousPriority) {
|
||||
RCTLogError(@"The RCTURLRequestHandlers %@ and %@ both reported that"
|
||||
@ -180,6 +183,7 @@ RCT_EXPORT_MODULE()
|
||||
}
|
||||
}
|
||||
}
|
||||
return previousHandler;
|
||||
}
|
||||
|
||||
// Normal code path
|
||||
|
@ -79,6 +79,12 @@ typedef void (^RCTFatalHandler)(NSError *error);
|
||||
#define RCTAssertMainThread() RCTAssert([NSThread isMainThread], \
|
||||
@"This function must be called on the main thread")
|
||||
|
||||
/**
|
||||
* Convenience macro for asserting that we're running off the main thread.
|
||||
*/
|
||||
#define RCTAssertNotMainThread() RCTAssert(![NSThread isMainThread], \
|
||||
@"This function must not be called on the main thread")
|
||||
|
||||
/**
|
||||
* These methods get and set the current assert function called by the RCTAssert
|
||||
* macros. You can use these to replace the standard behavior with custom assert
|
||||
|
Loading…
x
Reference in New Issue
Block a user