我们在Alamofire-URLSession中讲到过URLSession
实现的后台,我们用Alamofire
实现后台下载。
Alamofire后台下载实现
LGBackgroundManger.shared.manager
.download(self.urlDownloadStr) { (url, response) -> (destinationURL: URL, options: DownloadRequest.DownloadOptions) in
let documentUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let fileUrl = documentUrl?.appendingPathComponent(response.suggestedFilename!)
return (fileUrl!,[.removePreviousFile,.createIntermediateDirectories])
}
.response { (downloadResponse) in
print("下载回调信息: \(downloadResponse)")
}
.downloadProgress { (progress) in
print("下载进度 : \(progress)")
}
1.封装LGBackgroundManger
后台下载管理类.
2.通过Block
的方式实现下载进度的回调和下载完成方法的回调,不需要在实现代理方法。
struct LGBackgroundManger {
static let shared = LGBackgroundManger()
let manager: SessionManager = {
let configuration = URLSessionConfiguration.background(withIdentifier: "com.alamofire.text")
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
}
为什么要做成单例?
1.后台下载session
的配置URLSessionConfiguration
必须是background
模式
2.如果不被持有,进入后台被释放,报错Error Domain=NSURLErrorDomain Code=-999 "cancelled"
3.可以达到应用层与网络层分离
4.方便在AppDelegate的回调直接接收completionHandler
Alamofire
后台下载的流程和URLSession
后台下载流程一样,需要在AppDelegate实现backgroundSessionCompletionHandler
方法:
var backgroundSessionCompletionHandler: (() -> Void)?
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
print("hello - \(identifier)")
LGBackgroundManger.shared.manager.backgroundCompletionHandler = completionHandler
self.backgroundSessionCompletionHandler = completionHandler
}
那么我们还是有疑问,URLSession
会在下载完成后urlSessionDidFinishEvents
代理方法中,调用handleEventsForBackgroundURLSession
保存的completionHandler
达到下载后对屏幕的刷新。那么Alamofire
是如何处理的?我们分析一下SessionManger
的流程.
SessionManger流程分析
一、SessionManger的初始化
public init(
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
delegate: SessionDelegate = SessionDelegate(),
serverTrustPolicyManager: ServerTrustPolicyManager? = nil)
{
self.delegate = delegate
self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
commonInit(serverTrustPolicyManager: serverTrustPolicyManager)
}
1.configuration
配置,如果没有指定默认URLSessionConfiguration.default
2.SessionDelegate = SessionDelegate()
,将URLSession
代理设置为SessionDelegate
。代理移交给SessionDelegate。是一个重要的类,是多个代理的集合。
URLSessionTaskDelegate
URLSessionDataDelegate
URLSessionDownloadDelegate
URLSessionStreamDelegate
URLSessionDelegate
3.调用commonInit
初始化信息。
private func commonInit(serverTrustPolicyManager: ServerTrustPolicyManager?) {
session.serverTrustPolicyManager = serverTrustPolicyManager
delegate.sessionManager = self
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
guard let strongSelf = self else { return }
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
}
4.SessionDelegate
保存sessionManager
。
这里sessionManager
保存SessionDelegate
,SessionDelegate
也保存了sessionManager
的引用,会有循环引用吗? SessionDelegate
中weak var sessionManager: SessionManager?
保存SessionManager
是weak
修饰,所以不会造成循环引用.
5.为什么要在SessionDelegate
中保存sessionManager
?
(1)将sessionManager
比作是一个经理,SessionDelegate
比作是一个助理,经理分派任务给助理,助理处理任务,需要将处理结果反馈给经理。所以持有sessionManager
的引用,就能方便的进行沟通。
二、下载完成回调
sessionManager
将代理交给了SessionDelegate
,当任务下载完成后,将会来到SessionDelegate
的urlSessionDidFinishEvents
open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
sessionDidFinishEventsForBackgroundURLSession?(session)
}
urlSessionDidFinishEvents
调用sessionDidFinishEventsForBackgroundURLSession
闭包,sessionDidFinishEventsForBackgroundURLSession
是哪里初始化的?
我们找到了是在commonInit
中初始化这个闭包
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
guard let strongSelf = self else { return }
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
sessionDidFinishEventsForBackgroundURLSession
中在主线程中调用sessionManager
保存的backgroundCompletionHandler
闭包。这个闭包就是AppDelegate
调用handleEventsForBackgroundURLSession
方法时,保存进去的。
三、总结
-
sessionManager
初始化时,在commonInit
方法中初始化``SessionDelegate的
sessionDidFinishEventsForBackgroundURLSession `闭包。
delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
guard let strongSelf = self else { return }
DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
2.在appdelegate
中的handleEventsForBackgroundURLSession
方法中将返回的completionHandler
保存到sessionManager
的backgroundCompletionHandler
LGBackgroundManger.shared.manager.backgroundCompletionHandler = completionHandler
3.下载完成回调会来到SessionDelegate
的urlSessionDidFinishEvents
open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
sessionDidFinishEventsForBackgroundURLSession?(session)
}
4.urlSessionDidFinishEvents
中调用1
中SessionDelegate
保存的sessionDidFinishEventsForBackgroundURLSession
5.sessionDidFinishEventsForBackgroundURLSession
中执行2
中保存到sessionManager
的闭包。