Add one feature, to allow user to limit the progress interval and reduce the frequency of progress callback

Also fix that progressive decoding should stop when all image data is downloaded
This commit is contained in:
DreamPiggy 2018-08-02 12:08:18 +08:00
parent 1c89282b08
commit 67e0df75c6
5 changed files with 65 additions and 15 deletions

View File

@ -200,10 +200,18 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
} }
NSOperation<SDWebImageDownloaderOperation> *operation = [[operationClass alloc] initWithRequest:request inSession:sself.session options:options context:context]; NSOperation<SDWebImageDownloaderOperation> *operation = [[operationClass alloc] initWithRequest:request inSession:sself.session options:options context:context];
if (sself.config.urlCredential) { if ([operation respondsToSelector:@selector(setCredential:)]) {
operation.credential = sself.config.urlCredential; if (sself.config.urlCredential) {
} else if (sself.config.username && sself.config.password) { operation.credential = sself.config.urlCredential;
operation.credential = [NSURLCredential credentialWithUser:sself.config.username password:sself.config.password persistence:NSURLCredentialPersistenceForSession]; } else if (sself.config.username && sself.config.password) {
operation.credential = [NSURLCredential credentialWithUser:sself.config.username password:sself.config.password persistence:NSURLCredentialPersistenceForSession];
}
}
if ([operation respondsToSelector:@selector(setMinimumProgressInterval:)]) {
NSTimeInterval minimumProgressInterval = sself.config.minimumProgressInterval;
minimumProgressInterval = MIN(MAX(minimumProgressInterval, 0), 1);
operation.minimumProgressInterval = minimumProgressInterval;
} }
if (options & SDWebImageDownloaderHighPriority) { if (options & SDWebImageDownloaderHighPriority) {

View File

@ -41,6 +41,15 @@ typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
*/ */
@property (nonatomic, assign) NSTimeInterval downloadTimeout; @property (nonatomic, assign) NSTimeInterval downloadTimeout;
/**
* The minimum interval about progress percent during network downloading. Which means the next progress callback and current progress callback's progress percent difference should be larger or equal to this value.
* The value should be 0.0-1.0.
* @note If you're using progressive decoding feature, this will also effect the image refresh rate.
* @note This value may enhance the performance if you don't want progress callback too frequently.
* Defaults to 0, which means each time we receive the new data from URLSession, we callback the progressBlock immediately.
*/
@property (nonatomic, assign) NSTimeInterval minimumProgressInterval;
/** /**
* The custom session configuration in use by NSURLSession. If you don't provide one, we will use `defaultSessionConfiguration` instead. * The custom session configuration in use by NSURLSession. If you don't provide one, we will use `defaultSessionConfiguration` instead.
* Defatuls to nil. * Defatuls to nil.

View File

@ -34,6 +34,7 @@ static SDWebImageDownloaderConfig * _defaultDownloaderConfig;
SDWebImageDownloaderConfig *config = [[[self class] allocWithZone:zone] init]; SDWebImageDownloaderConfig *config = [[[self class] allocWithZone:zone] init];
config.maxConcurrentDownloads = self.maxConcurrentDownloads; config.maxConcurrentDownloads = self.maxConcurrentDownloads;
config.downloadTimeout = self.downloadTimeout; config.downloadTimeout = self.downloadTimeout;
config.minimumProgressInterval = self.minimumProgressInterval;
config.sessionConfiguration = [self.sessionConfiguration copyWithZone:zone]; config.sessionConfiguration = [self.sessionConfiguration copyWithZone:zone];
config.operationClass = self.operationClass; config.operationClass = self.operationClass;
config.executionOrder = self.executionOrder; config.executionOrder = self.executionOrder;

View File

@ -36,9 +36,6 @@ FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadFinishNotification
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock; completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
- (nullable NSURLCredential *)credential;
- (void)setCredential:(nullable NSURLCredential *)value;
- (BOOL)cancel:(nullable id)token; - (BOOL)cancel:(nullable id)token;
- (nullable NSURLRequest *)request; - (nullable NSURLRequest *)request;
@ -46,6 +43,10 @@ FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadFinishNotification
@optional @optional
- (nullable NSURLSessionTask *)dataTask; - (nullable NSURLSessionTask *)dataTask;
- (nullable NSURLCredential *)credential;
- (void)setCredential:(nullable NSURLCredential *)credential;
- (NSTimeInterval)minimumProgressInterval;
- (void)setMinimumProgressInterval:(NSTimeInterval)minimumProgressInterval;
@end @end
@ -74,6 +75,15 @@ FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadFinishNotification
*/ */
@property (nonatomic, strong, nullable) NSURLCredential *credential; @property (nonatomic, strong, nullable) NSURLCredential *credential;
/**
* The minimum interval about progress percent during network downloading. Which means the next progress callback and current progress callback's progress percent difference should be larger or equal to this value.
* The value should be 0.0-1.0.
* @note If you're using progressive decoding feature, this will also effect the image refresh rate.
* @note This value may enhance the performance if you don't want progress callback too frequently.
* Defaults to 0, which means each time we receive the new data from URLSession, we callback the progressBlock immediately.
*/
@property (assign, nonatomic) NSTimeInterval minimumProgressInterval;
/** /**
* The options for the receiver. * The options for the receiver.
*/ */

View File

@ -37,9 +37,11 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
@property (assign, nonatomic, getter = isFinished) BOOL finished; @property (assign, nonatomic, getter = isFinished) BOOL finished;
@property (strong, nonatomic, nullable) NSMutableData *imageData; @property (strong, nonatomic, nullable) NSMutableData *imageData;
@property (copy, nonatomic, nullable) NSData *cachedData; // for `SDWebImageDownloaderIgnoreCachedResponse` @property (copy, nonatomic, nullable) NSData *cachedData; // for `SDWebImageDownloaderIgnoreCachedResponse`
@property (assign, nonatomic, readwrite) NSUInteger expectedSize; @property (assign, nonatomic) NSUInteger expectedSize; // may be 0
@property (assign, nonatomic) NSUInteger receivedSize;
@property (strong, nonatomic, nullable, readwrite) NSURLResponse *response; @property (strong, nonatomic, nullable, readwrite) NSURLResponse *response;
@property (strong, nonatomic, nullable) NSError *responseError; @property (strong, nonatomic, nullable) NSError *responseError;
@property (assign, nonatomic) double previousProgress; // previous progress percent
// This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run // This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run
// the task associated with this operation // the task associated with this operation
@ -332,16 +334,36 @@ didReceiveResponse:(NSURLResponse *)response
} }
[self.imageData appendData:data]; [self.imageData appendData:data];
if ((self.options & SDWebImageDownloaderProgressiveLoad) && self.expectedSize > 0) { self.receivedSize = self.imageData.length;
if (self.expectedSize == 0) {
// Unknown expectedSize, immediately call progressBlock and return
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(self.receivedSize, self.expectedSize, self.request.URL);
}
return;
}
// Get the finish status
BOOL finished = (self.receivedSize >= self.expectedSize);
// Get the current progress
double currentProgress = (double)self.receivedSize / (double)self.expectedSize;
double previousProgress = self.previousProgress;
// Check if we need callback progress
if (currentProgress - previousProgress < self.minimumProgressInterval) {
return;
}
self.previousProgress = currentProgress;
if (self.options & SDWebImageDownloaderProgressiveLoad) {
// Get the image data // Get the image data
NSData *imageData = [self.imageData copy]; NSData *imageData = [self.imageData copy];
// Get the total bytes downloaded
const NSUInteger totalSize = imageData.length;
// Get the finish status
BOOL finished = (totalSize >= self.expectedSize);
// progressive decode the image in coder queue // progressive decode the image in coder queue
dispatch_async(self.coderQueue, ^{ dispatch_async(self.coderQueue, ^{
// If all the data has already been downloaded, earily return to avoid further decoding
if (self.receivedSize >= self.expectedSize) {
return;
}
UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, finished, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context); UIImage *image = SDImageLoaderDecodeProgressiveImageData(imageData, self.request.URL, finished, self, [[self class] imageOptionsFromDownloaderOptions:self.options], self.context);
if (image) { if (image) {
// We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding. // We do not keep the progressive decoding image even when `finished`=YES. Because they are for view rendering but not take full function from downloader options. And some coders implementation may not keep consistent between progressive decoding and normal decoding.
@ -352,7 +374,7 @@ didReceiveResponse:(NSURLResponse *)response
} }
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) { for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(self.imageData.length, self.expectedSize, self.request.URL); progressBlock(self.receivedSize, self.expectedSize, self.request.URL);
} }
} }