Merge pull request #2937 from dreampiggy/feature_URLSessionTaskMetrics

Added the URLSessionTaskMetrics support for downloader && operation, which can be used for network metrics
This commit is contained in:
DreamPiggy 2020-02-12 11:45:50 +08:00 committed by GitHub
commit bbfe690845
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 6 deletions

View File

@ -113,10 +113,22 @@
}
cell.customTextLabel.text = [NSString stringWithFormat:@"Image #%ld", (long)indexPath.row];
[cell.customImageView sd_setImageWithURL:[NSURL URLWithString:self.objects[indexPath.row]]
placeholderImage:placeholderImage
options:indexPath.row == 0 ? SDWebImageRefreshCached : 0
context:@{SDWebImageContextImageThumbnailPixelSize : @(CGSizeMake(180, 120))}];
__weak SDAnimatedImageView *imageView = cell.customImageView;
[imageView sd_setImageWithURL:[NSURL URLWithString:self.objects[indexPath.row]]
placeholderImage:placeholderImage
options:indexPath.row == 0 ? SDWebImageRefreshCached : 0
context:@{SDWebImageContextImageThumbnailPixelSize : @(CGSizeMake(180, 120))}
progress:nil
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
SDWebImageCombinedOperation *operation = [imageView sd_imageLoadOperationForKey:imageView.sd_latestOperationKey];
SDWebImageDownloadToken *token = operation.loaderOperation;
if (@available(iOS 10.0, *)) {
NSURLSessionTaskMetrics *metrics = token.metrics;
if (metrics) {
printf("Metrics: %s download in (%f) seconds\n", [imageURL.absoluteString cStringUsingEncoding:NSUTF8StringEncoding], metrics.taskInterval.duration);
}
}
}];
return cell;
}

View File

@ -128,6 +128,11 @@ typedef SDImageLoaderCompletedBlock SDWebImageDownloaderCompletedBlock;
*/
@property (nonatomic, strong, nullable, readonly) NSURLResponse *response;
/**
The download's metrics. This will be nil if download operation does not support metrics.
*/
@property (nonatomic, strong, nullable, readonly) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
@end

View File

@ -24,6 +24,7 @@ static void * SDWebImageDownloaderContext = &SDWebImageDownloaderContext;
@property (nonatomic, strong, nullable, readwrite) NSURL *url;
@property (nonatomic, strong, nullable, readwrite) NSURLRequest *request;
@property (nonatomic, strong, nullable, readwrite) NSURLResponse *response;
@property (nonatomic, strong, nullable, readwrite) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
@property (nonatomic, weak, nullable, readwrite) id downloadOperationCancelToken;
@property (nonatomic, weak, nullable) NSOperation<SDWebImageDownloaderOperation> *downloadOperation;
@property (nonatomic, assign, getter=isCancelled) BOOL cancelled;
@ -498,6 +499,15 @@ didReceiveResponse:(NSURLResponse *)response
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)) {
// Identify the operation that runs this task and pass it the delegate method
NSOperation<SDWebImageDownloaderOperation> *dataOperation = [self operationWithTask:task];
if ([dataOperation respondsToSelector:@selector(URLSession:task:didFinishCollectingMetrics:)]) {
[dataOperation URLSession:session task:task didFinishCollectingMetrics:metrics];
}
}
@end
@implementation SDWebImageDownloadToken
@ -510,18 +520,30 @@ didReceiveResponse:(NSURLResponse *)response
self = [super init];
if (self) {
_downloadOperation = downloadOperation;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadReceiveResponse:) name:SDWebImageDownloadReceiveResponseNotification object:downloadOperation];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadDidReceiveResponse:) name:SDWebImageDownloadReceiveResponseNotification object:downloadOperation];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadDidStop:) name:SDWebImageDownloadStopNotification object:downloadOperation];
}
return self;
}
- (void)downloadReceiveResponse:(NSNotification *)notification {
- (void)downloadDidReceiveResponse:(NSNotification *)notification {
NSOperation<SDWebImageDownloaderOperation> *downloadOperation = notification.object;
if (downloadOperation && downloadOperation == self.downloadOperation) {
self.response = downloadOperation.response;
}
}
- (void)downloadDidStop:(NSNotification *)notification {
NSOperation<SDWebImageDownloaderOperation> *downloadOperation = notification.object;
if (downloadOperation && downloadOperation == self.downloadOperation) {
if ([downloadOperation respondsToSelector:@selector(metrics)]) {
if (@available(iOS 10.0, tvOS 10.0, macOS 10.12, watchOS 3.0, *)) {
self.metrics = downloadOperation.metrics;
}
}
}
}
- (void)cancel {
@synchronized (self) {
if (self.isCancelled) {

View File

@ -36,6 +36,7 @@
@optional
@property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;
@property (strong, nonatomic, readonly, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
@property (strong, nonatomic, nullable) NSURLCredential *credential;
@property (assign, nonatomic) double minimumProgressInterval;
@ -62,6 +63,12 @@
*/
@property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;
/**
* The collected metrics from `-URLSession:task:didFinishCollectingMetrics:`.
* This can be used to collect the network metrics like download duration, DNS lookup duration, SSL handshake dureation, etc. See Apple's documentation: https://developer.apple.com/documentation/foundation/urlsessiontaskmetrics
*/
@property (strong, nonatomic, readonly, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
/**
* The credential used for authentication challenges in `-URLSession:task:didReceiveChallenge:completionHandler:`.
*

View File

@ -52,6 +52,8 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
@property (strong, nonatomic, readwrite, nullable) NSURLSessionTask *dataTask;
@property (strong, nonatomic, readwrite, nullable) NSURLSessionTaskMetrics *metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
@property (strong, nonatomic, nonnull) dispatch_queue_t coderQueue; // the queue to do image decoding
#if SD_UIKIT
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
@ -512,6 +514,10 @@ didReceiveResponse:(NSURLResponse *)response
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)) {
self.metrics = metrics;
}
#pragma mark Helper methods
+ (SDWebImageOptions)imageOptionsFromDownloaderOptions:(SDWebImageDownloaderOptions)downloadOptions {
SDWebImageOptions options = 0;

View File

@ -31,6 +31,13 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima
*/
@property (nonatomic, strong, readonly, nullable) NSURL *sd_imageURL;
/**
* Get the current image operation key. Operation key is used to identify the different queries for one view instance (like UIButton).
* See more about this in `SDWebImageContextSetImageOperationKey`.
* @note You can use method `UIView+WebCacheOperation` to invesigate different queries' operation.
*/
@property (nonatomic, strong, readonly, nullable) NSString *sd_latestOperationKey;
/**
* The current image loading progress associated to the view. The unit count is the received size and excepted size of download.
* The `totalUnitCount` and `completedUnitCount` will be reset to 0 after a new image loading start (change from current queue). And they will be set to `SDWebImageProgressUnitCountUnknown` if the progressBlock not been called but the image loading success to mark the progress finished (change from main queue).

View File

@ -642,6 +642,37 @@
}];
}
- (void)test26DownloadURLSessionMetrics {
XCTestExpectation *expectation1 = [self expectationWithDescription:@"Download URLSessionMetrics works"];
SDWebImageDownloader *downloader = [[SDWebImageDownloader alloc] init];
__block SDWebImageDownloadToken *token;
token = [downloader downloadImageWithURL:[NSURL URLWithString:kTestJPEGURL] completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
expect(error).beNil();
if (@available(iOS 10.0, tvOS 10.0, macOS 10.12, *)) {
NSURLSessionTaskMetrics *metrics = token.metrics;
expect(metrics).notTo.beNil();
expect(metrics.redirectCount).equal(0);
expect(metrics.transactionMetrics.count).equal(1);
NSURLSessionTaskTransactionMetrics *metric = metrics.transactionMetrics.firstObject;
// Metrcis Test
expect(metric.fetchStartDate).notTo.beNil();
expect(metric.connectStartDate).notTo.beNil();
expect(metric.connectEndDate).notTo.beNil();
expect(metric.networkProtocolName).equal(@"http/1.1");
expect(metric.resourceFetchType).equal(NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad);
expect(metric.isProxyConnection).beFalsy();
expect(metric.isReusedConnection).beFalsy();
}
[expectation1 fulfill];
}];
[self waitForExpectationsWithCommonTimeoutUsingHandler:^(NSError * _Nullable error) {
[downloader invalidateSessionAndCancel:YES];
}];
}
#pragma mark - SDWebImageLoader
- (void)test30CustomImageLoaderWorks {
XCTestExpectation *expectation = [self expectationWithDescription:@"Custom image not works"];