Alamofire-后台下载及SessionManger流程分析

我们在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的引用,会有循环引用吗? SessionDelegateweak var sessionManager: SessionManager?保存SessionManagerweak修饰,所以不会造成循环引用.

5.为什么要在SessionDelegate中保存sessionManager?
(1)将sessionManager比作是一个经理,SessionDelegate比作是一个助理,经理分派任务给助理,助理处理任务,需要将处理结果反馈给经理。所以持有sessionManager的引用,就能方便的进行沟通。

二、下载完成回调

sessionManager将代理交给了SessionDelegate,当任务下载完成后,将会来到SessionDelegateurlSessionDidFinishEvents

 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方法时,保存进去的。

三、总结
  1. sessionManager初始化时,在commonInit 方法中初始化``SessionDelegatesessionDidFinishEventsForBackgroundURLSession `闭包。
 delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
            guard let strongSelf = self else { return }
            DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
        }

2.在appdelegate中的handleEventsForBackgroundURLSession方法中将返回的completionHandler保存到sessionManagerbackgroundCompletionHandler

  LGBackgroundManger.shared.manager.backgroundCompletionHandler = completionHandler

3.下载完成回调会来到SessionDelegateurlSessionDidFinishEvents

  open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        sessionDidFinishEventsForBackgroundURLSession?(session)
    }

4.urlSessionDidFinishEvents中调用1SessionDelegate保存的sessionDidFinishEventsForBackgroundURLSession
5.sessionDidFinishEventsForBackgroundURLSession中执行2中保存到sessionManager的闭包。

你可能感兴趣的:(Alamofire-后台下载及SessionManger流程分析)