SDWebImage 源代码剖析-网络部分

前面两篇博文中,简要对SDWebImage的缓存部分多线程部分进行了分析。建议在阅读本篇内容前先看下缓存策略那篇,以对SDWebImage的基本内容有所了解。在本篇中,我们将对它的网络策略进行分析。我们知道SDWebImage的主要功能就是从远程主机上下载图片,所以前面的几个策略都是为这一目的提供支持的,而网络策略的好坏将直接决定库的性能。不过SDWebImage以GitHub上接近20000的star数向我们证明了它不俗的实力,下面就让我们一起看看吧。

本文共分为两部分,第一部分是对NSURLSession的介绍,第二部分是对SDWebImage中网络部分的介绍。

NSURLSession

NSURLSession是苹果在2013年的WWDC上推出的,目的是取代老迈的前任NSURLConnection。它包含了一系列组件,包括NSURLRequestNSURLCacheNSURLSessionConfigurationNSURLSessionTask等类。大名鼎鼎的AFNetworking以及本文要重点介绍的SDWebImage内部都大量使用NSURLSession来完成网络请求功能,因此了解NSURLSession是很有必要的。
如果没有什么额外需求,那么使用NSURLSession来发出一次网络请求是非常简单的:

这里我们使用的是均为默认配置。如果我们想在进行网络请求时进行一些自定义,那么我们就需要使用NSURLSessionConfiguration类来配置我们的NSURLSession

NSURLSessionConfiguration

NSURLSessionConfiguration可以配置的东西很多,比如缓存策略、时延、连接要求、Cookies等等,几乎你能想到的东西都可以进行配置。熟练掌握这些可配置选项的用法和意义对于我们控制连接的行为重要。
networkServiceType:类型是NSURLRequestNetworkServiceType,指明了所处的网络环境,系统可能会针对性地使用不同的性能模式、电量模式;allowsCellularAccess,BOOL类型,指明连接是否可以通过蜂窝数据进行。某些APP如微博、知乎等提供只通过WiFi浏览的功能,其实就是利用这个属性来实现的;
timeoutIntervalForRequest,类型是NSTimeInterval,指明了所有task的超时时间。在这个时间之内,task会继续等待;当超过这个时间后,task会认为本次连接失败了,默认是60秒。

在初始化一个NSURLSession实例的时候我们传入一个NSURLSessionConfiguration实例,session会将这个configuration拷贝到自身中,一旦初始化完成了,对configuration进行修改并不会改变session的配置。如果我们要使用新的配置,那么我们只能重新生成一个NSURLSession实例。
NSURLSessionConfiguration提供了三个工厂方法:

  1. defaultSessionConfiguration
    这个是默认配置,它会将缓存存储在磁盘上、将凭证信息存储在用户的keychain以及使用全局的cookie信息。
  2. ephemeralSessionConfiguration
    这个配置不会将缓存、凭证信息、cookies等存储在磁盘上,而是存储在内存中,因此这些数据会被自动清除释放。因此当我们想要实现无痕浏览等功能的话就可以使用这个配置。
  3. backgroundSessionConfigurationWithIdentifier:
    这个配置允许应用进入后台时仍能进行下载上传工作。当使用这个配置时,session会把控制权交给系统,系统在另外的进程中进行数据传输。当应用停止并重启后,参数identifier可以用来恢复原来传输过程的长下文。不过呢,这个恢复也是有条件的,只有应用是由系统停止的,在停止后才能继续在后台传输;如果应用是由用户在多任务界面关掉的话,那么系统会取消掉所有后台传输。

使用NSURLConfiguration来初始化一个NSURLSession的代码如下:

NSURLSessionTask

NSURLSession字面上的意思是“一次会话”,在一次会话中可能会有多次请求,而每一次具体的请求是使用NSURLSessionTask来完成的。同一个NSURLSession可以创建多个task,并且这些task之间的cache 和cookie是共享的。NSURLSession 为我们提供了三种类型的task,分别是:

  1. NSURLSessionDataTask:用于一般类型的数据传输,如客户端读取服务器返回的请求。
  2. NSURLSessionDownloadTask:用于下载任务,它的内部针对大文件的网络请求做了更多处理。
  3. NSURLSessionUploadTask:用于上传任务,它主要用于客户端向服务器发送数据,如用户上传头像、发送照片等场景

Delegates

NSURLSession为我们提供了种类多样功能丰富的代理方法,以便我们能够自定义地处理网络请求过程中的各种情况。比如NSURLSessionDataDelegate中,
- URLSession:dataTask:didReceiveResponse:completionHandler:可以通知我们已经收到了服务器的响应;
NSURLSessionDownloadDelegate中,URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:可以告诉我们总共需要接收多少数据、现在已经接收多少数据,从而我们能够计算出下载的进度;NSURLSessionDownloadDelegate中的
- URLSession:downloadTask:didFinishDownloadingToURL:可以告诉我们下载已经完成并告诉我们下载的临时文件的地址。这里的介绍,只是抛砖引玉,更多详细的内容可以去查阅URL Session Programming Guide以及NSURLSession的相关文档。

小结

上面的内容简要介绍了NSURLSession这一系列API的使用方法,我们接下来会看到,SDWebImage的网络请求部分完全构建在NSURLSession之上,因此了解进而掌握NSURLSession这一系列框架是非常有必要的。

SDWebImageDownloader

我们开始正式对SDWebImage中的网络部分进行分析。为了行文的流畅性以及清晰性,本篇内容与之前的博客内容会有所重复。
首先来看一下SDWebImageDownloader这个类。在SDWebImageDownloader.m中可以看到SDWebImageDownloader的一个扩展:

这个类实现了NSURLSessionTaskDelegateNSURLSessionDataDelegate两个协议,显然它就是实际进行网络下载的类了。

初始化SDWebImageDownloader

SDWebImageDownloader有一个便捷构造器和一个指定构造器:

下载过程

该方法其实是调用了下面的这个方法,而//这部分的代码是createCallback这个block块的内容。我们先看一下下面这个方法里做了什么。

从这个方法中我们可以看出self.URLOperation这个属性,是以URL为key以operation为value的一个键值对,而且由于operation会在自己执行完毕时将自己移除出键值对,因此self.URLOperation中的operation都处在尚未执行完毕的状态。
我们再回过头来看看在- (nullable SDWebImageDownloadToken *)downloadImageWithURL:
options:progress:completed:completedBlock
方法中createCallback的内容。

  1. 使用SDWebImageDownloaderOptions来配置一个NSURLRequest对象
  2. 创建一个SDWebImageDownloaderOperation对象并进行配置
  3. 将第二步中创建的operation加入到self.downloadQueue中。如果我们使用的执行策略是后入先出,也就是说上一个operation会在当前的这个operation执行完才执行,因此我们把上一个operation的依赖设置为当前operation。

协议实现

上面提到过SDWebImageDownloader这个类实现了NSURLSessionTaskDelegateNSURLSessionDataDelegate两个协议。但是当我们去查看实现时我们发现这些协议方法内都是调用了SDWebImageDownloaderOperation对象的同名方法。

小结

从代码中我们发现,SDWebImageDownloader完成的核心功能无非就是管理着self.URLOperations键值对,而协议方法其实都是由SDWebImageDownloaderOperation来完成的。

SDWebImageDownloaderOperation

这个类比较关键的属性有如下几个:

因为该类继承了NSOperation,因此也实现了startcancel等方法。

NSURLSessionDataDelegate

NSURLSessionDataDelegate共定义了五个方法,SDWebImageDownloadOperation实现了其中的三个:

NSURLSessionTaskDelegate

NSURLSessionTaskDelegate中定义了八个方法,而SDWebImageDownloadOperation实现了其中两个:

总结

本文简单介绍了NSURLSession的使用,并分析了它在SDWebImage中的应用。至此,对SDWebImage这一框架的分析将告一段落。在分析SDWebImage源码的过程中,我曾数次被这一框架的优点打动:接口简洁易用,结构优美逻辑强,代码清晰易懂,注释详细丰富,值得再三学习品味。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据