美文网首页
AFNetWorking 下载图片过程阅读笔记

AFNetWorking 下载图片过程阅读笔记

作者: 杨柳小易 | 来源:发表于2017-09-06 17:58 被阅读132次

###AFNetWorking ****下载图片过程

[self.imageView setImageWithURL:[NSURL URLWithString:item.picture] placeholderImage:[UIImage imageNamed:@"PTV_Normal_Default_Icon"]];

比如上面的执行过程:

会调用如下:

- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(UIImage *)placeholderImage
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];

    [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}

生成 request,并设置http 请求头 accept 字段值为 image/*

我们都知道,http header 消息通常被分为4个部分:general header, request header, response header, entity header。但是这种分法就理解而言,感觉界限不太明确。根据维基百科对http header内容的组织形式,大体分为Request和Response两部分。

<strong>Accept 字段 表示:指定客户端能够接收的内容类型 </strong>
http 头中还有一个比较常用的字段 Cache-Control,用来指定缓存策略。
关于 http 头的,详见 httpHead详解 w3c官网Header Field Definitions

扯远了,不过看一遍还是有帮助的。

下载过程如下:

- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
              placeholderImage:(UIImage *)placeholderImage
                       success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
                       failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
{

    if ([urlRequest URL] == nil) {
        [self cancelImageDownloadTask];
        self.image = placeholderImage;
        return;
    }

    if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
        return;
    }

    [self cancelImageDownloadTask];

    AFImageDownloader *downloader = [[self class] sharedImageDownloader];
    id <AFImageRequestCache> imageCache = downloader.imageCache;

    //Use the image from the image cache if it exists
    UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
    if (cachedImage) {
        if (success) {
            success(urlRequest, nil, cachedImage);
        } else {
            self.image = cachedImage;
        }
        [self clearActiveDownloadInformation];
    } else {
        if (placeholderImage) {
            self.image = placeholderImage;
        }

        __weak __typeof(self)weakSelf = self;
        NSUUID *downloadID = [NSUUID UUID];
        AFImageDownloadReceipt *receipt;
        receipt = [downloader
                   downloadImageForURLRequest:urlRequest
                   withReceiptID:downloadID
                   success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
                       __strong __typeof(weakSelf)strongSelf = weakSelf;
                       if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                           if (success) {
                               success(request, response, responseObject);
                           } else if(responseObject) {
                               strongSelf.image = responseObject;
                           }
                           [strongSelf clearActiveDownloadInformation];
                       }

                   }
                   failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
                       __strong __typeof(weakSelf)strongSelf = weakSelf;
                        if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
                            if (failure) {
                                failure(request, response, error);
                            }
                            [strongSelf clearActiveDownloadInformation];
                        }
                   }];

        self.af_activeImageDownloadReceipt = receipt;
    }
}

###****接下来分析一下上面那个长函数

AFNetworking 的所有图片下载都是通过同一个downloader 实例完成的,这里用的是默认的。

AFImageDownloader *downloader = [[self class] sharedImageDownloader];

+ (AFImageDownloader *)sharedImageDownloader {
    return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}

+ (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader {
    objc_setAssociatedObject(self, @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

这里通过关联和 class 绑定的。这里的 [AFImageDownloader defaultInstance] 就有一些默认的配置。比如 defaultURLCache等等。

但是下载过程还是通过AF的组件 AFHTTPSessionManager 来完成的。

- (instancetype)init {
    NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration];
    AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:defaultConfiguration];
    sessionManager.responseSerializer = [AFImageResponseSerializer serializer];

    return [self initWithSessionManager:sessionManager
                 downloadPrioritization:AFImageDownloadPrioritizationFIFO
                 maximumActiveDownloads:4
                             imageCache:[[AFAutoPurgingImageCache alloc] init]];
}

最重要的,这里解析数据的指定成了图片解析。

sessionManager.responseSerializer = [AFImageResponseSerializer serializer];

AFImageResponseSerializer 提供了图片解码的过程。

AFHTTPSessionManager 优秀的设计方案

@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;

这里请求和响应的property 都是遵守某个协议的,方便对不同类型的请求做不同的处理,比如:xml json, image. <strong>这在我们平常的类中可以参考</strong>

获取到了下载实例,接下来尝试从缓存中获取图片

UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
    if (cachedImage) {
        if (success) {
            success(urlRequest, nil, cachedImage);
        } else {
            self.image = cachedImage;
        }
        
        //代表下载完成。
        [self clearActiveDownloadInformation];

如果没有缓存,就通过AFImageDownloader 下载数据,因为解析出来的数据已经处理过了,所以直接赋值image 就可以了!

###****缓存篇
这里缓存默认使用的是 NSCache..

+ (NSURLCache *)defaultURLCache {
    // It's been discovered that a crash will occur on certain versions
    // of iOS if you customize the cache.
    //
    // More info can be found here: https://devforums.apple.com/message/1102182#1102182
    //
    // When iOS 7 support is dropped, this should be modified to use
    // NSProcessInfo methods instead.
    if ([[[UIDevice currentDevice] systemVersion] compare:@"8.2" options:NSNumericSearch] == NSOrderedAscending) {
        return [NSURLCache sharedURLCache];
    }
    return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
                                         diskCapacity:150 * 1024 * 1024
                                             diskPath:@"com.alamofire.imagedownloader"];
}

我在本地项目试了一下如下测试代码:

NSLog(@"🐒🐒🐒🐒  %lu %lu",(unsigned long)[NSURLCache sharedURLCache].memoryCapacity, (unsigned long)[NSURLCache sharedURLCache].diskCapacity);

输出如下:

2017-09-06 17:30:32.928311+0800 PandaTV-HD[1173:464474] 🐒🐒🐒🐒  512000 10000000

也就是说,其实默认就已经设置好了512kb的内存缓存空间,以及10MB的磁盘缓存空间。可能你的代码中并没有写任何与NSURLCache有关的东西,但其实它已经默默的开始帮你进行缓存了。

关于 NSCache 详见两篇比较好的博客 DIY图片缓存库 NSUrlCache详解

###****下载队列篇
下载队列创建如下:

        self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);

创建的是一个串行队列。

配合

typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
    AFImageDownloadPrioritizationFIFO,
    AFImageDownloadPrioritizationLIFO
};

可以实现,下载队列的顺序,比如:

- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
    switch (self.downloadPrioritizaton) {
        case AFImageDownloadPrioritizationFIFO:
            [self.queuedMergedTasks addObject:mergedTask];
            break;
        case AFImageDownloadPrioritizationLIFO:
            [self.queuedMergedTasks insertObject:mergedTask atIndex:0];
            break;
    }
}

self.queuedMergedTasks 是一个数组。

在上个一请求完成,会尝试开启下一个请求。

- (void)safelyStartNextTaskIfNecessary {
    dispatch_sync(self.synchronizationQueue, ^{
        if ([self isActiveRequestCountBelowMaximumLimit]) {
            while (self.queuedMergedTasks.count > 0) {
                AFImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask];
                if (mergedTask.task.state == NSURLSessionTaskStateSuspended) {
                    [self startMergedTask:mergedTask];
                    break;
                }
            }
        }
    });
}

请求都是在同一个线程里 同步进行的,所以不用使用NSLock 这样子的东东。

相关文章

网友评论

      本文标题:AFNetWorking 下载图片过程阅读笔记

      本文链接:https://www.haomeiwen.com/subject/rzyvjxtx.html