Alamofire-URLSession

对于使用Objective-C开发者,一定非常熟悉AFNetworking这个网络框架。在苹果推出的Swift之后,AFNetworking的作者专门用Swift来编写一个类似AFNetworking的网络框架,称为Alamofire。它是基于URLSession封装的,因为在了解Alamofire之前,我们先来复习一些URLSession知识.

一、网络请求的基本格式

        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error == nil {
                print("请求成功\(String(describing: response))" )
            }
        }.resume()

1.创建URLSession
2.创建dataTask(url)
3.发起请求resume

二、URLSessionConfiguration

open class URLSession : NSObject {

    public /*not inherited*/ init(configuration: URLSessionConfiguration)

    public /*not inherited*/ init(configuration: URLSessionConfiguration, delegate: URLSessionDelegate?, delegateQueue queue: OperationQueue?)
}

通过指定URLSessionConfiguration创建URLSession。下面我们看一下URLSessionConfiguration有些什么配置项。

open class URLSessionConfiguration : NSObject, NSCopying {

    
    open class var `default`: URLSessionConfiguration { get }

    open class var ephemeral: URLSessionConfiguration { get }

    
    @available(iOS 8.0, *)
    open class func background(withIdentifier identifier: String) -> URLSessionConfiguration
}

首先我们看到创建URLSessionConfiguration,包含三种模式default,ephemeral ``background,它们之间有什么区别呢?

        let configuration1 = URLSessionConfiguration.default
        let configuration2 = URLSessionConfiguration.ephemeral
        print("沙盒大小: \(String(describing: configuration1.urlCache?.diskCapacity))")
        print("内存大小: \(String(describing: configuration1.urlCache?.memoryCapacity))")
        print("沙盒大小: \(String(describing: configuration2.urlCache?.diskCapacity))")
        print("内存大小: \(String(describing: configuration2.urlCache?.memoryCapacity))")

打印结果:

沙盒大小: Optional(10000000)
内存大小: Optional(512000)
沙盒大小: Optional(0)
内存大小: Optional(512000)

.default模式比.ephemeral模式多了100K的沙盒容量。这个容量用来做什么?.default模式下系统会创建一个持久化的缓存(缓存就放置到这个沙盒容量)并在用户的钥匙串中存储证书。
ephemeral模式下 系统没有任何持久性存储,所有内容的生命周期都与session相同,当session无效时,所有内容自动释放。
再来看看.background模式,.background创建一个可以在后台甚至APP已经关闭的时候仍然在传输数据的会话。.background模式与.default模式非常相似,不过background模式会用一个独立线程来进行数据传输。background模式可以在程序挂起,退出,崩溃的情况下运行task。也可以利用标识符来恢复。注意,后台Session一定要在创建的时候赋予一个唯一的identifier,这样在APP下次运行的时候,能够根据identifier来进行相关的区分。如果用户关闭了APP,IOS 系统会关闭所有的background Session。而且,被用户强制关闭了以后,IOS系统不会主动唤醒APP,只有用户下次启动了APP,数据传输才会继续。

     let configuration = URLSessionConfiguration.background(withIdentifier: self.createID())

        let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
        
        session.downloadTask(with: url).resume()

后台下载使用步骤:
1.初始化一个后台的模式的会话配置
2.初始化session会话
3.传入url开启下载resume
4.实现URLSessionDownloadDelegate代理的下载进度监控

  func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {

5.实现URLSessionDownloadDelegate代理的下载完成方法

  func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL)

开启后台下载这里还有2个点需要注意:
1.需要开启后台下载权限:

    var backgroundSessionCompletionHandler: (() -> Void)?

    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        self.backgroundSessionCompletionHandler = completionHandler
    }
实现`handleEventsForBackgroundURLSession`才可以完美后台下载

2.需要回调系统,告知系统及时更新屏幕
实现NSURLSessionDelegate代理的urlSessionDidFinishEventsforBackgroundURLSession:方法

   func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        print("后台任务下载回来")
        DispatchQueue.main.async {
            guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
            backgroundHandle()
        }
    }

那么如果不实现这个代理里面的回调函数的执行会有什么问题?
1.后台下载的能力是不会影响的
2.但是会爆出非常验证界面刷新卡顿,影响用户体验
3.同时打印台会爆出警告

Warning: Application delegate received call to -
application:handleEventsForBackgroundURLSession:completionHandler: 
but the completion handler was never called.

URLSessionConfiguration除了模式的指定,还有很多其他的属性设置。我们分为常规属性、Cookie设置、安全策略设置、缓存策略、支持后台转移、支持自定义协议、 支持多路径TCP、设置HTTP策略和代理属性、支持连接变化等

常规属性
identifier:配置对象的后台会话标识符。
httpAdditionalHeaders:与请求一起发送的附加头文件的字典。
networkServiceType:网络服务的类型
allowsCellularAccess:一个布尔值,用于确定是否应通过蜂窝网络进行连接。
timeoutIntervalForRequest:等待其他数据时使用的超时间隔。
timeoutIntervalForResource:资源请求应该允许的最大时间量。
sharedContainerIdentifier:应该下载后台URL会话中的文件的共享容器的标识符。
waitsForConnectivity:一个布尔值,指示会话是否应等待连接变为可用或者立即失败

设置Cookie政策
httpCookieAcceptPolicy:决定何时应该接受Cookie的策略常量
httpShouldSetCookies:一个布尔值,用于确定请求是否应包含来自Cookie存储的Cookie。
httpCookieStorage:管理cookie存储的单一对象(共享实例)
HTTPCookie:表示HTTP cookie的对象。它是一个不可变的对象,从包含cookie属性的字典中初始化

设置安全策略
tlsMaximumSupportedProtocol:在此会话中进行连接时客户端应请求的最大TLS协议版本。
tlsMinimumSupportedProtocol:协议协商期间应该接受的最小TLS协议。
urlCredentialStorage:提供身份验证凭据的凭证存储

设置缓存策略
urlCache:用于向会话中的请求提供缓存响应的URL缓存
requestCachePolicy:一个预定义常量,用于确定何时从缓存中返回响应

支持后台转移
sessionSendsLaunchEvents:一个布尔值,指示在传输完成时是否应该在后台继续或启动应用程序
isDiscretionary:一个布尔值,用于确定是否可以根据系统的判断来调度后台任务以获得最佳性能。

支持自定义协议
protocolClasses:在会话中处理请求的额外协议子类的数组
URLProtocol:一个NSURLProtocol对象处理加载协议特定的URL数据。在NSURLProtocol类本身是一个抽象类,可以为与特定URL方案的URL处理基础设施。您可以为您的应用支持的任何自定义协议或URL方案创建子类

支持多路径TCP
multipathServiceType:指定用于通过Wi-Fi和蜂窝接口传输数据的多路径TCP连接策略的服务类型
URLSessionConfiguration.MultipathServiceType:指定多路径TCP使用的服务类型的常量

设置HTTP策略和代理属性

httpMaximumConnectionsPerHost:同时连接到给定主机的最大数量。
httpShouldUsePipelining:一个布尔值,用于确定会话是否应使用HTTP流水线
connectionProxyDictionary:包含有关在此会话中使用的代理信息的字典

支持连接变化

waitsForConnectivity:一个布尔值,指示会话是否应等待连接变为可用或者立即失败。

三、NSURLRequestCachePolicy

typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    NSURLRequestUseProtocolCachePolicy = 0,

    NSURLRequestReloadIgnoringLocalCacheData = 1,
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

    NSURLRequestReturnCacheDataElseLoad = 2,
    NSURLRequestReturnCacheDataDontLoad = 3,

    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};

1.NSURLRequestUseProtocolCachePolicy
默认的缓存策略,其行为是由协议指定的针对该协议最好的实现方式。

机制.png

(1)如果请求的缓存响应不存在,则URL加载系统直接从源端加载数据;
(2)否则,如果缓存响应中没有明确表示每次请求必须重新验证,则如果不是响应的缓存过期了,则URL加载系统会返回缓存数据
(3)如果缓存的响应过期或者需要重新验证,URL加载系统发送HEAD请求到源端,查看资源是否发生了变化。如果变化了,则URL加载系统取出从始发源的数据。否则,它返回缓存的响应。

缓存的响应过期或者需要重新验证说明:
对于缓存的响应过期或者需要重新验证的情况,可以通过HTTP中请求和响应头判断:
Cache-Control
在第一次请求到服务器资源的时候,服务器需要使用Cache-Control这个响应头来指定缓存策略,它的格式如下:Cache-Control:max-age=xxxx,这个头指明缓存过期的时间。

Cache-Control头具有如下选项:
public
指示响应可被任何缓存区缓存

private
内容只缓存到私有缓存中(仅客户端可以缓存)

no-cache
指示请求或响应消息不能缓存

no-store
所有内容都不会被缓存到缓存或 Internet 临时文件中

must-revalidation
如果缓存的内容失效,请求必须发送到服务器进行重新验证

max-age
可以接收生存期不大于指定时间(以秒为单位)的响应

min-fresh
可以接收响应时间小于当前时间加上指定时间的响应

max-stale
可以接收超出超时期间的响应消息

Expires
Expires表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同max-age的效果。但是如果同时存在,则被Cache-Control的max-age覆盖。
格式:Expires = "Expires" ":" HTTP-date"
例如:Expires: Thu, 01 Dec 1994 16:00:00 GMT (必须是GMT格式)
Last-Modified/If-Modified-Since
Last-Modified是由服务器返回响应头,标识资源的最后修改时间.
If-Modified-Since 则由客户端发送,标识客户端所记录的,资源的最后修改时间。服务器接收到带有该请求头的请求时,会使用该时间与资源的最后修改时间进行对比,如果发现资源未被修改过,则直接返回HTTP 304而不返回包体,告诉客户端直接使用本地的缓存。否则响应完整的消息内容。

Etag/If-None-Match
Etag 由服务器发送,告之当资源在服务器上的一个唯一标识符。
客户端请求时,如果发现资源过期(使用Cache-Control的max-age),发现资源具有Etag声明,这时请求服务器时则带上If-None-Match头,服务器收到后则与资源的标识进行对比,决定返回200或者304。

2.NSURLRequestReloadIgnoringCacheData
从服务端加载数据,完全忽略缓存。
3.NSURLRequestReturnCacheDataElseLoad
使用缓存数据,忽略其过期时间;只有在没有缓存版本的时候才从源端加载数据。
4.NSURLRequestReturnCacheDataDontLoad
只使用cache数据,如果不存在cache,请求失败;用于没有建立网络连接离线模式

你可能感兴趣的:(Alamofire-URLSession)