Skip to content

Commit

Permalink
Add cache and scheduling parameters to image loaders
Browse files Browse the repository at this point in the history
  • Loading branch information
janicduplessis committed Jun 14, 2016
1 parent 654778c commit e837958
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 24 deletions.
12 changes: 12 additions & 0 deletions Libraries/Image/RCTImageLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,18 @@ __deprecated_msg("Use getImageSizeWithURLRequest:block: instead");
*/
- (float)loaderPriority;

/**
* If the loader must be called on the serial url cache queue. If this is NO,
* the loader will be called from the main queue. Defaults to YES.
*/
- (BOOL)requiresScheduling;

/**
* If images loaded by the loader should be cached in the decoded image cache.
* Defaults to YES.
*/
- (BOOL)shouldCacheLoadedImages;

@end

/**
Expand Down
57 changes: 36 additions & 21 deletions Libraries/Image/RCTImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -290,27 +290,44 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest *
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
completionBlock:(void (^)(NSError *error, id imageOrData))completionBlock
completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult))completionBlock
{
__block volatile uint32_t cancelled = 0;
__block void(^cancelLoad)(void) = nil;
__weak RCTImageLoader *weakSelf = self;
// Find suitable image URL loader
__block id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:imageURLRequest.URL];
__block BOOL cacheResult = [loadHandler respondsToSelector:@selector(shouldCacheLoadedImages)] ?
[loadHandler shouldCacheLoadedImages] : YES;
BOOL requiresScheduling = [loadHandler respondsToSelector:@selector(requiresScheduling)] ?
[loadHandler requiresScheduling] : YES;

void (^completionHandler)(NSError *error, id imageOrData) = ^(NSError *error, id imageOrData) {
if (RCTIsMainQueue()) {
if (RCTIsMainQueue() && requiresScheduling) {

// Most loaders do not return on the main thread, so caller is probably not
// expecting it, and may do expensive post-processing in the callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (!cancelled) {
completionBlock(error, imageOrData);
completionBlock(error, imageOrData, cacheResult);
}
});
} else if (!cancelled) {
completionBlock(error, imageOrData);
completionBlock(error, imageOrData, cacheResult);
}
};

// If the loader doesn't require scheduling we call it directly on
// the main queue.
if (loadHandler && !requiresScheduling) {
return [loadHandler loadImageForURL:imageURLRequest.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
completionHandler:completionHandler] ?: ^{};
}

// All access to URL cache must be serialized
if (!_URLCacheQueue) {
[self setUp];
Expand All @@ -328,9 +345,18 @@ - (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest *
return;
}

// Find suitable image URL loader
NSURLRequest *request = imageURLRequest; // Use a local variable so we can reassign it in this block

if (loadHandler && requiresScheduling) {
cancelLoad = [loadHandler loadImageForURL:imageURLRequest.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
completionHandler:completionHandler] ?: ^{};
return;
}

// Check if networking module is available
if (RCT_DEBUG && ![_bridge respondsToSelector:@selector(networking)]) {
RCTLogError(@"No suitable image URL loader found for %@. You may need to "
Expand Down Expand Up @@ -470,18 +496,6 @@ - (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)image
__block void(^cancelLoad)(void) = nil;
__weak RCTImageLoader *weakSelf = self;

// Load handlers are executed before the generic caching logic so they are responsible
// for caching the images if necessary.
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:imageURLRequest.URL];
if (loadHandler) {
return [loadHandler loadImageForURL:imageURLRequest.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
completionHandler:completionBlock] ?: ^{};
}

// Check decoded image cache
NSString *cacheKey = RCTCacheKeyForImage(imageURLRequest.URL.absoluteString, size, scale, resizeMode);
{
Expand All @@ -506,17 +520,18 @@ - (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)image
completionBlock(error, image);
};

void (^completionHandler)(NSError *, id) = ^(NSError *error, id imageOrData) {
void (^completionHandler)(NSError *, id, BOOL) = ^(NSError *error, id imageOrData, BOOL cacheResult) {
if (!cancelled) {
RCTImageLoaderCompletionBlock resultHandler = cacheResult ? cacheResultHandler : completionBlock;
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
cacheResultHandler(error, imageOrData);
resultHandler(error, imageOrData);
} else {
cancelLoad = [weakSelf decodeImageData:imageOrData
size:size
scale:scale
clipped:clipped
resizeMode:resizeMode
completionBlock:cacheResultHandler];
completionBlock:resultHandler];
}
}
};
Expand Down Expand Up @@ -654,7 +669,7 @@ - (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)ima
scale:1
resizeMode:RCTResizeModeStretch
progressBlock:nil
completionBlock:^(NSError *error, id imageOrData) {
completionBlock:^(NSError *error, id imageOrData, __unused BOOL cacheResult) {
CGSize size;
if ([imageOrData isKindOfClass:[NSData class]]) {
NSDictionary *meta = RCTGetImageMetadata(imageOrData);
Expand Down
17 changes: 14 additions & 3 deletions Libraries/Image/RCTLocalAssetImageLoader.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ - (BOOL)canLoadImageURL:(NSURL *)requestURL
return RCTIsLocalAssetURL(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;
}

- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
Expand All @@ -37,9 +51,6 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
}

NSString *imageName = RCTBundlePathForURL(imageURL);

// imageNamed handles the caching automatically so there is no
// need to do any manual caching.
UIImage *image = [UIImage imageNamed:imageName];
if (image) {
if (progressHandler) {
Expand Down

0 comments on commit e837958

Please sign in to comment.