Authored by 刘金林

performance report for sdwebimage

... ... @@ -10,8 +10,6 @@
@interface SDWebImageManager (AutoTrack)
@property(nonatomic,copy)NSString *yh_webImageId;//唯一ID
+(void)startTrack;
@end
... ...
... ... @@ -7,87 +7,112 @@
//
#import "SDWebImageManager+AutoTrack.h"
#import "YHSwizzle.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "YH_EventCollector.h"
#import "YHLog.h"
#import "YHEventReport.h"
@interface NSNumber (YH_UnitConvert)
@implementation SDWebImageManager (AutoTrack)
@property (nonatomic, copy, readonly) NSNumber *SToIntMS;//s -> ms 并取整
@end
@implementation NSNumber (YH_UnitConvert)
- (NSNumber *)SToIntMS {
const double ms = self.doubleValue;
const double s = ms * 1000;
return @((NSUInteger)s);
}
@end
@interface NSURLSessionTaskTransactionMetrics (YH_TrackEvent)
@property (nonatomic, copy, readonly) NSNumber *totalTime;
@property (nonatomic, copy, readonly) NSNumber *dnsTime;
@property (nonatomic, copy, readonly) NSNumber *connectTime;
@property (nonatomic, copy, readonly) NSNumber *secureConnectTime;
@property (nonatomic, copy, readonly) NSNumber *requestTime;
@property (nonatomic, copy, readonly) NSNumber *responseTime;
@end
@implementation NSURLSessionTaskTransactionMetrics (YH_TrackEvent)
- (NSNumber *)timeIntervalFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate {
if (fromDate && toDate &&
[fromDate compare:toDate] == NSOrderedAscending) {
return @([toDate timeIntervalSinceDate:fromDate]);
}
return @(0.f);
}
- (NSNumber *)totalTime {
return [self timeIntervalFromDate:self.fetchStartDate toDate:self.responseEndDate];
}
- (NSNumber *)dnsTime {
return [self timeIntervalFromDate:self.domainLookupStartDate toDate:self.domainLookupEndDate];
}
- (void)setYh_webImageId:(NSString *)yh_webImageId{
objc_setAssociatedObject(self,@selector(yh_webImageId),yh_webImageId,OBJC_ASSOCIATION_RETAIN);
- (NSNumber *)connectTime {
return [self timeIntervalFromDate:self.connectStartDate toDate:self.connectEndDate];
}
- (NSString *)yh_webImageId{
return objc_getAssociatedObject(self, @selector(yh_webImageId));
- (NSNumber *)secureConnectTime {
return [self timeIntervalFromDate:self.secureConnectionStartDate toDate:self.secureConnectionEndDate];
}
- (NSNumber *)requestTime {
return [self timeIntervalFromDate:self.requestStartDate toDate:self.requestEndDate];
}
- (NSNumber *)responseTime {
return [self timeIntervalFromDate:self.responseStartDate toDate:self.responseEndDate];
}
@end
@implementation SDWebImageManager (AutoTrack)
+(void)startTrack
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@try {
NSError *downloadImageError = NULL;
[[self class] yh_swizzleMethod:@selector(downloadImageWithURL:options:progress:completed:)
withMethod:@selector(yher_downloadImageWithURL:options:progress:completed:)
error:&downloadImageError];
if (downloadImageError) {
downloadImageError = NULL;
}
} @catch (NSException *exception) {
YHLog(@"%@ error: %@", self, exception);
}
[[NSNotificationCenter defaultCenter] addObserverForName:@"yoho_image_download_network_metrics" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self reportImageViewPerformanceForNotification:note];
});
}];
});
}
- (id <SDWebImageOperation>)yher_downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDInternalCompletionBlock)completedBlock
{
@try {
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
if ([url isKindOfClass:NSURL.class]) {
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isImagePerformanceTrackEnable){
[[YH_EventCollector sharedInstance] timeEventStartWithWebImageDownloadURL:url.absoluteString];
}
}
} @catch (NSException *exception) {
YHLog(@"%@ error: %@", self, exception);
+ (void)reportImageViewPerformanceForNotification:(NSNotification *)note {
NSURLSessionTaskMetrics *metrics = note.userInfo[@"metrics"];
NSError *error = note.userInfo[@"error"];
NSParameterAssert([metrics isKindOfClass:[NSURLSessionTaskMetrics class]]);
if (error.code == NSURLErrorCancelled ) {
return;
}
return [self yher_downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
@try {
if ([url isKindOfClass:NSURL.class]) {
if ([YHEventReport sharedInstance].isPerformanceTrackEnabled && [YHEventReport sharedInstance].isImagePerformanceTrackEnable){
if (cacheType == SDImageCacheTypeNone) {//暂时只去下载的图片的时间,过滤掉读缓存的时间
if (error) {
[[YH_EventCollector sharedInstance] timeEventEndWithWebImageDownloadURL:url.absoluteString status:YHEventLoadStatusFailed];
}else{
[[YH_EventCollector sharedInstance] timeEventEndWithWebImageDownloadURL:url.absoluteString status:YHEventLoadStatusSuc];
}
}
}
}
} @catch (NSException *exception) {
YHLog(@"%@ error: %@", self, exception);
}
if (completedBlock) {
completedBlock(image,data,error,cacheType,finished,imageURL);
[metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
[self reportImageDownloadTransactionMetrics:obj];
}
}];
}
+ (void)reportImageDownloadTransactionMetrics:(NSURLSessionTaskTransactionMetrics *)transMetrics {
NSDictionary *performanceDict = @{ @"url" : transMetrics.request.URL.absoluteString ?: @"",
@"protocol": transMetrics.networkProtocolName ?:@"",
@"proxyConnection": transMetrics.proxyConnection ? @"1" : @"0",
@"reusedConnection": transMetrics.reusedConnection ? @"1" : @"0",
@"fetch": transMetrics.totalTime.SToIntMS,
@"dns": transMetrics.dnsTime.SToIntMS,
@"connect": transMetrics.connectTime.SToIntMS,
@"secureConnection": transMetrics.secureConnectTime.SToIntMS,
@"request": transMetrics.requestTime.SToIntMS,
@"response": transMetrics.responseTime.SToIntMS
};
[[YHEventReport sharedInstance] reportWithPointType:YHPT_PERFORMANCE pointName:YHPN_IMAGEVIEW parameters:performanceDict];
}
@end
... ...