美文网首页
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