Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

Commit

Permalink
Add download resume support (#3246)
Browse files Browse the repository at this point in the history
* Adds support for resuming downloads that were canceled due to exiting preload range.

* Update to latest PINRemoteImage

* Fix warnings

* Address comments. Thanks @nguyenhuy!
  • Loading branch information
garrettmoon committed Apr 10, 2017
1 parent 582bca9 commit 68a8d5f
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 17 deletions.
2 changes: 1 addition & 1 deletion AsyncDisplayKit.podspec
Expand Up @@ -44,7 +44,7 @@ Pod::Spec.new do |spec|
end

spec.subspec 'PINRemoteImage' do |pin|
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.8'
pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.9'
pin.dependency 'PINRemoteImage/PINCache'
pin.dependency 'AsyncDisplayKit/Core'
end
Expand Down
30 changes: 18 additions & 12 deletions Source/ASNetworkImageNode.mm
Expand Up @@ -58,6 +58,7 @@ @interface ASNetworkImageNode ()
unsigned int downloaderImplementsSetProgress:1;
unsigned int downloaderImplementsSetPriority:1;
unsigned int downloaderImplementsAnimatedImage:1;
unsigned int downloaderImplementsCancelWithResume:1;
} _downloaderFlags;

// Immutable and set on init only. We don't need to lock in this case.
Expand Down Expand Up @@ -85,6 +86,7 @@ - (instancetype)initWithCache:(id<ASImageCacheProtocol>)cache downloader:(id<ASI
_downloaderFlags.downloaderImplementsSetProgress = [downloader respondsToSelector:@selector(setProgressImageBlock:callbackQueue:withDownloadIdentifier:)];
_downloaderFlags.downloaderImplementsSetPriority = [downloader respondsToSelector:@selector(setPriority:withDownloadIdentifier:)];
_downloaderFlags.downloaderImplementsAnimatedImage = [downloader respondsToSelector:@selector(animatedImageWithData:)];
_downloaderFlags.downloaderImplementsCancelWithResume = [downloader respondsToSelector:@selector(cancelImageDownloadWithResumePossibilityForIdentifier:)];

_cacheFlags.cacheSupportsClearing = [cache respondsToSelector:@selector(clearFetchedImageFromCacheWithURL:)];
_cacheFlags.cacheSupportsSynchronousFetch = [cache respondsToSelector:@selector(synchronouslyFetchedCachedImageWithURL:)];
Expand All @@ -107,7 +109,7 @@ - (instancetype)init

- (void)dealloc
{
[self _cancelImageDownload];
[self _cancelImageDownloadWithResumePossibility:NO];
}

#pragma mark - Public methods -- must lock
Expand All @@ -127,7 +129,7 @@ - (void)_locked_setImage:(UIImage *)image
if (shouldCancelAndClear) {
ASDisplayNodeAssertNil(_URL, @"Directly setting an image on an ASNetworkImageNode causes it to behave like an ASImageNode instead of an ASNetworkImageNode. If this is what you want, set the URL to nil first.");
_URL = nil;
[self _locked_cancelDownloadAndClearImage];
[self _locked_cancelDownloadAndClearImageWithResumePossibility:NO];
}

[self _locked__setImage:image];
Expand Down Expand Up @@ -163,7 +165,7 @@ - (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset
return;
}

[self _locked_cancelImageDownload];
[self _locked_cancelImageDownloadWithResumePossibility:NO];

_imageLoaded = NO;

Expand Down Expand Up @@ -385,7 +387,7 @@ - (void)didExitPreloadState
return;
}

[self _cancelDownloadAndClearImage];
[self _cancelDownloadAndClearImageWithResumePossibility:YES];
}

- (void)didEnterPreloadState
Expand Down Expand Up @@ -469,15 +471,15 @@ - (void)_updateProgressImageBlockOnDownloaderIfNeeded
}
}

- (void)_cancelDownloadAndClearImage
- (void)_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResume
{
ASDN::MutexLocker l(__instanceLock__);
[self _locked_cancelDownloadAndClearImage];
[self _locked_cancelDownloadAndClearImageWithResumePossibility:storeResume];
}

- (void)_locked_cancelDownloadAndClearImage
- (void)_locked_cancelDownloadAndClearImageWithResumePossibility:(BOOL)storeResume
{
[self _locked_cancelImageDownload];
[self _locked_cancelImageDownloadWithResumePossibility:storeResume];

// Destruction of bigger images on the main thread can be expensive
// and can take some time, so we dispatch onto a bg queue to
Expand All @@ -504,20 +506,24 @@ - (void)_locked_cancelDownloadAndClearImage
}
}

- (void)_cancelImageDownload
- (void)_cancelImageDownloadWithResumePossibility:(BOOL)storeResume
{
ASDN::MutexLocker l(__instanceLock__);
[self _locked_cancelImageDownload];
[self _locked_cancelImageDownloadWithResumePossibility:storeResume];
}

- (void)_locked_cancelImageDownload
- (void)_locked_cancelImageDownloadWithResumePossibility:(BOOL)storeResume
{
if (!_downloadIdentifier) {
return;
}

if (_downloadIdentifier) {
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
if (storeResume && _downloaderFlags.downloaderImplementsCancelWithResume) {
[_downloader cancelImageDownloadWithResumePossibilityForIdentifier:_downloadIdentifier];
} else {
[_downloader cancelImageDownloadForIdentifier:_downloadIdentifier];
}
}
_downloadIdentifier = nil;

Expand Down
11 changes: 11 additions & 0 deletions Source/Details/ASImageProtocols.h
Expand Up @@ -109,6 +109,17 @@ typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {

@optional

/**
@abstract Cancels an image download, however indicating resume data should be stored in case of redownload.
@param downloadIdentifier The opaque download identifier object returned from
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
@discussion This method has no effect if `downloadIdentifier` is nil. If implemented, this method
may be called instead of `cancelImageDownloadForIdentifier:` in cases where ASDK believes there's a chance
the image download will be resumed (currently when an image exits preload range). You can use this to store
any data that has already been downloaded for use in resuming the download later.
*/
- (void)cancelImageDownloadWithResumePossibilityForIdentifier:(id)downloadIdentifier;

/**
@abstract Return an object that conforms to ASAnimatedImageProtocol
@param animatedImageData Data that represents an animated image.
Expand Down
14 changes: 10 additions & 4 deletions Source/Details/ASPINRemoteImageDownloader.m
Expand Up @@ -245,7 +245,13 @@ - (nullable id)downloadImageWithURL:(NSURL *)URL
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier
{
ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID");
[[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier];
[[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier storeResumeData:NO];
}

- (void)cancelImageDownloadWithResumePossibilityForIdentifier:(id)downloadIdentifier
{
ASDisplayNodeAssert([downloadIdentifier isKindOfClass:[NSUUID class]], @"downloadIdentifier must be NSUUID");
[[self sharedPINRemoteImageManager] cancelTaskWithUUID:downloadIdentifier storeResumeData:YES];
}

- (void)setProgressImageBlock:(ASImageDownloaderProgressImage)progressBlock callbackQueue:(dispatch_queue_t)callbackQueue withDownloadIdentifier:(id)downloadIdentifier
Expand All @@ -270,15 +276,15 @@ - (void)setPriority:(ASImageDownloaderPriority)priority withDownloadIdentifier:(
PINRemoteImageManagerPriority pi_priority = PINRemoteImageManagerPriorityMedium;
switch (priority) {
case ASImageDownloaderPriorityPreload:
pi_priority = PINRemoteImageManagerPriorityMedium;
pi_priority = PINRemoteImageManagerPriorityLow;
break;

case ASImageDownloaderPriorityImminent:
pi_priority = PINRemoteImageManagerPriorityHigh;
pi_priority = PINRemoteImageManagerPriorityDefault;
break;

case ASImageDownloaderPriorityVisible:
pi_priority = PINRemoteImageManagerPriorityVeryHigh;
pi_priority = PINRemoteImageManagerPriorityHigh;
break;
}
[[self sharedPINRemoteImageManager] setPriority:pi_priority ofTaskWithUUID:downloadIdentifier];
Expand Down

0 comments on commit 68a8d5f

Please sign in to comment.