mirror of
https://github.com/status-im/react-native.git
synced 2025-01-10 17:45:59 +00:00
060664fd3d
Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
169 lines
5.7 KiB
Objective-C
169 lines
5.7 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import "RCTAssetsLibraryImageLoader.h"
|
|
|
|
#import <AssetsLibrary/AssetsLibrary.h>
|
|
#import <ImageIO/ImageIO.h>
|
|
#import <libkern/OSAtomic.h>
|
|
#import <UIKit/UIKit.h>
|
|
|
|
#import "RCTBridge.h"
|
|
#import "RCTConvert.h"
|
|
#import "RCTImageLoader.h"
|
|
#import "RCTImageUtils.h"
|
|
#import "RCTUtils.h"
|
|
|
|
static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void);
|
|
|
|
@implementation RCTAssetsLibraryImageLoader
|
|
{
|
|
ALAssetsLibrary *_assetsLibrary;
|
|
}
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
@synthesize bridge = _bridge;
|
|
|
|
- (ALAssetsLibrary *)assetsLibrary
|
|
{
|
|
return _assetsLibrary ?: (_assetsLibrary = [ALAssetsLibrary new]);
|
|
}
|
|
|
|
#pragma mark - RCTImageLoader
|
|
|
|
- (BOOL)canLoadImageURL:(NSURL *)requestURL
|
|
{
|
|
return [requestURL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame;
|
|
}
|
|
|
|
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
|
|
size:(CGSize)size
|
|
scale:(CGFloat)scale
|
|
resizeMode:(UIViewContentMode)resizeMode
|
|
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
|
|
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
|
{
|
|
__block volatile uint32_t cancelled = 0;
|
|
|
|
[[self assetsLibrary] assetForURL:imageURL resultBlock:^(ALAsset *asset) {
|
|
if (cancelled) {
|
|
return;
|
|
}
|
|
|
|
if (asset) {
|
|
// ALAssetLibrary API is async and will be multi-threaded. Loading a few full
|
|
// resolution images at once will spike the memory up to store the image data,
|
|
// and might trigger memory warnings and/or OOM crashes.
|
|
// To improve this, process the loaded asset in a serial queue.
|
|
dispatch_async(RCTAssetsLibraryImageLoaderQueue(), ^{
|
|
if (cancelled) {
|
|
return;
|
|
}
|
|
|
|
// Also make sure the image is released immediately after it's used so it
|
|
// doesn't spike the memory up during the process.
|
|
@autoreleasepool {
|
|
BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero);
|
|
ALAssetRepresentation *representation = [asset defaultRepresentation];
|
|
|
|
#if RCT_DEV
|
|
|
|
CGSize sizeBeingLoaded = size;
|
|
if (useMaximumSize) {
|
|
CGSize pointSize = representation.dimensions;
|
|
sizeBeingLoaded = CGSizeMake(pointSize.width * representation.scale, pointSize.height * representation.scale);
|
|
}
|
|
|
|
CGSize screenSize;
|
|
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedDescending) {
|
|
screenSize = [UIScreen mainScreen].nativeBounds.size;
|
|
} else {
|
|
CGSize mainScreenSize = [UIScreen mainScreen].bounds.size;
|
|
CGFloat mainScreenScale = [[UIScreen mainScreen] scale];
|
|
screenSize = CGSizeMake(mainScreenSize.width * mainScreenScale, mainScreenSize.height * mainScreenScale);
|
|
}
|
|
CGFloat maximumPixelDimension = fmax(screenSize.width, screenSize.height);
|
|
|
|
if (sizeBeingLoaded.width > maximumPixelDimension || sizeBeingLoaded.height > maximumPixelDimension) {
|
|
RCTLogInfo(@"[PERF ASSETS] Loading %@ at size %@, which is larger than screen size %@",
|
|
representation.filename, NSStringFromCGSize(sizeBeingLoaded), NSStringFromCGSize(screenSize));
|
|
}
|
|
|
|
#endif
|
|
|
|
UIImage *image = nil;
|
|
NSError *error = nil;
|
|
if (useMaximumSize) {
|
|
|
|
image = [UIImage imageWithCGImage:representation.fullResolutionImage
|
|
scale:scale
|
|
orientation:(UIImageOrientation)representation.orientation];
|
|
} else {
|
|
|
|
NSUInteger length = (NSUInteger)representation.size;
|
|
uint8_t *buffer = (uint8_t *)malloc((size_t)length);
|
|
if ([representation getBytes:buffer
|
|
fromOffset:0
|
|
length:length
|
|
error:&error]) {
|
|
|
|
NSData *data = [[NSData alloc] initWithBytesNoCopy:buffer
|
|
length:length
|
|
freeWhenDone:YES];
|
|
|
|
image = RCTDecodeImageWithData(data, size, scale, resizeMode);
|
|
} else {
|
|
free(buffer);
|
|
}
|
|
}
|
|
|
|
completionHandler(error, image);
|
|
}
|
|
});
|
|
} else {
|
|
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageURL];
|
|
completionHandler(RCTErrorWithMessage(errorText), nil);
|
|
}
|
|
} failureBlock:^(NSError *loadError) {
|
|
if (cancelled) {
|
|
return;
|
|
}
|
|
|
|
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageURL, loadError];
|
|
completionHandler(RCTErrorWithMessage(errorText), nil);
|
|
}];
|
|
|
|
return ^{
|
|
OSAtomicOr32Barrier(1, &cancelled);
|
|
};
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTBridge (RCTAssetsLibraryImageLoader)
|
|
|
|
- (ALAssetsLibrary *)assetsLibrary
|
|
{
|
|
return [[self moduleForClass:[RCTAssetsLibraryImageLoader class]] assetsLibrary];
|
|
}
|
|
|
|
@end
|
|
|
|
static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void)
|
|
{
|
|
static dispatch_queue_t queue;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
queue = dispatch_queue_create("com.facebook.RCTAssetsLibraryImageLoader", DISPATCH_QUEUE_SERIAL);
|
|
});
|
|
|
|
return queue;
|
|
}
|