mirror of
https://github.com/zhigang1992/PINRemoteImage.git
synced 2026-03-29 15:59:12 +08:00
Add support for retrying and reduce default timeout. (#215)
* Add support for retrying and reduce default timeout. * Return HTTP error codes >= 400. Don't retry when failure is sure to occur again. * Add missing nullability * Use NSInteger
This commit is contained in:
@@ -284,9 +284,8 @@
|
||||
{
|
||||
NSError *outError = result.error;
|
||||
|
||||
XCTAssert([outError.domain isEqualToString:NSURLErrorDomain]);
|
||||
XCTAssert(outError.code == NSURLErrorRedirectToNonExistentLocation);
|
||||
XCTAssert([outError.localizedDescription isEqualToString:@"The requested URL was not found on this server."]);
|
||||
XCTAssert([outError.domain isEqualToString:PINURLErrorDomain]);
|
||||
XCTAssert(outError.code == 404);
|
||||
|
||||
[expectation fulfill];
|
||||
}];
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
@property (nonatomic, assign) BOOL hasProgressBlocks;
|
||||
@property (nonatomic, strong, nullable) PINProgressiveImage *progressImage;
|
||||
|
||||
@property (nonatomic, assign) NSUInteger numberOfRetries;
|
||||
|
||||
- (void)callProgressDownloadWithQueue:(nonnull dispatch_queue_t)queue completedBytes:(int64_t)completedBytes totalBytes:(int64_t)totalBytes;
|
||||
- (void)callProgressImageWithQueue:(nonnull dispatch_queue_t)queue withImage:(nonnull PINImage *)image renderedImageQuality:(CGFloat)renderedImageQuality;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_canSetDataTaskPriority = [NSURLSessionTask instancesRespondToSelector:@selector(setPriority:)];
|
||||
_numberOfRetries = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
#import "NSData+ImageDetectors.h"
|
||||
#import "PINImage+DecodedImage.h"
|
||||
|
||||
#define PINRemoteImageManagerDefaultTimeout 60.0
|
||||
#define PINRemoteImageManagerDefaultTimeout 30.0
|
||||
#define PINRemoteImageMaxRetries 3
|
||||
#define PINRemoteImageRetryDelayBase 4
|
||||
|
||||
//A limit of 200 characters is chosen because PINDiskCache
|
||||
//may expand the length by encoding certain characters
|
||||
@@ -696,7 +698,7 @@ static dispatch_once_t sharedDispatchToken;
|
||||
{
|
||||
[self lock];
|
||||
PINRemoteImageDownloadTask *task = [self.tasks objectForKey:key];
|
||||
if (task.urlSessionTaskOperation == nil && task.callbackBlocks.count > 0) {
|
||||
if (task.urlSessionTaskOperation == nil && task.callbackBlocks.count > 0 && task.numberOfRetries == 0) {
|
||||
//If completionBlocks.count == 0, we've canceled before we were even able to start.
|
||||
CFTimeInterval startTime = CACurrentMediaTime();
|
||||
PINDataTaskOperation *urlSessionTaskOperation = [self sessionTaskWithURL:url key:key options:options priority:priority];
|
||||
@@ -750,9 +752,9 @@ static dispatch_once_t sharedDispatchToken;
|
||||
}
|
||||
|
||||
- (PINDataTaskOperation *)sessionTaskWithURL:(NSURL *)URL
|
||||
key:(NSString *)key
|
||||
options:(PINRemoteImageManagerDownloadOptions)options
|
||||
priority:(PINRemoteImageManagerPriority)priority
|
||||
key:(NSString *)key
|
||||
options:(PINRemoteImageManagerDownloadOptions)options
|
||||
priority:(PINRemoteImageManagerPriority)priority
|
||||
{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
return [self downloadDataWithURL:URL
|
||||
@@ -767,7 +769,36 @@ static dispatch_once_t sharedDispatchToken;
|
||||
PINImage *image = nil;
|
||||
id alternativeRepresentation = nil;
|
||||
|
||||
if (remoteImageError == nil) {
|
||||
if (remoteImageError && [[self class] retriableError:remoteImageError]) {
|
||||
//attempt to retry after delay
|
||||
BOOL retry = NO;
|
||||
NSUInteger newNumberOfRetries = 0;
|
||||
[strongSelf lock];
|
||||
PINRemoteImageDownloadTask *task = [self.tasks objectForKey:key];
|
||||
if (task.numberOfRetries < PINRemoteImageMaxRetries) {
|
||||
retry = YES;
|
||||
newNumberOfRetries = ++task.numberOfRetries;
|
||||
task.urlSessionTaskOperation = nil;
|
||||
}
|
||||
[strongSelf unlock];
|
||||
|
||||
if (retry) {
|
||||
int64_t delay = powf(PINRemoteImageRetryDelayBase, newNumberOfRetries);
|
||||
PINLog(@"Retrying download of %@ in %d seconds.", URL, delay);
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
typeof(self) strongSelf = weakSelf;
|
||||
[strongSelf lock];
|
||||
PINRemoteImageDownloadTask *task = [strongSelf.tasks objectForKey:key];
|
||||
if (task.urlSessionTaskOperation == nil && task.callbackBlocks.count > 0) {
|
||||
//If completionBlocks.count == 0, we've canceled before we were even able to start.
|
||||
PINDataTaskOperation *urlSessionTaskOperation = [strongSelf sessionTaskWithURL:URL key:key options:options priority:priority];
|
||||
task.urlSessionTaskOperation = urlSessionTaskOperation;
|
||||
}
|
||||
[strongSelf unlock];
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (remoteImageError == nil) {
|
||||
//stores the object in the caches
|
||||
[strongSelf materializeAndCacheObject:data cacheInDisk:data additionalCost:0 key:key options:options outImage:&image outAltRep:&alternativeRepresentation];
|
||||
}
|
||||
@@ -783,6 +814,17 @@ static dispatch_once_t sharedDispatchToken;
|
||||
}];
|
||||
}
|
||||
|
||||
+ (BOOL)retriableError:(NSError *)remoteImageError
|
||||
{
|
||||
if ([remoteImageError.domain isEqualToString:PINURLErrorDomain]) {
|
||||
return remoteImageError.code >= 500;
|
||||
} else if ([remoteImageError.domain isEqualToString:NSURLErrorDomain] && remoteImageError.code == NSURLErrorUnsupportedURL) {
|
||||
return NO;
|
||||
} else if ([remoteImageError.domain isEqualToString:PINRemoteImageManagerErrorDomain]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (PINDataTaskOperation *)downloadDataWithURL:(NSURL *)url
|
||||
key:(NSString *)key
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
extern NSString * __nonnull const PINURLErrorDomain;
|
||||
|
||||
@protocol PINURLSessionManagerDelegate <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#import "PINURLSessionManager.h"
|
||||
|
||||
NSString * const PINURLErrorDomain = @"PINURLErrorDomain";
|
||||
|
||||
@interface PINURLSessionManager () <NSURLSessionDelegate, NSURLSessionDataDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSLock *sessionManagerLock;
|
||||
@@ -114,10 +116,13 @@
|
||||
[self lock];
|
||||
dispatch_queue_t delegateQueue = self.delegateQueues[@(task.taskIdentifier)];
|
||||
[self unlock];
|
||||
if (!error && [task.response isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse *)task.response statusCode] == 404) {
|
||||
error = [NSError errorWithDomain:NSURLErrorDomain
|
||||
code:NSURLErrorRedirectToNonExistentLocation
|
||||
userInfo:@{NSLocalizedDescriptionKey : @"The requested URL was not found on this server."}];
|
||||
if (!error && [task.response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSInteger statusCode = [(NSHTTPURLResponse *)task.response statusCode];
|
||||
if (statusCode >= 400) {
|
||||
error = [NSError errorWithDomain:PINURLErrorDomain
|
||||
code:statusCode
|
||||
userInfo:@{NSLocalizedDescriptionKey : @"HTTP Error Response."}];
|
||||
}
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(delegateQueue, ^{
|
||||
|
||||
Reference in New Issue
Block a user