Alamofire-SessionManager解析

2019-08-19  本文已影响0人  SPIREJ

一、SessionManager初始化

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)
}

我们看到SessionManager类里面的初始化方法init

创建具有指定的“配置”“委托”“serverTrustPolicyManager”的实例。

其中初始化的时候有delegate.sessionManager = self
即:SessionDelegate.sessionManager = SessionManager,代理移交

所以我们将来在网络请求后的代理方法全部走SessionDelegate类,里面已经封装好了网络的代理方法

二、后台下载

二(1). 先说URLSession是如何处理后台下载

// 1:初始化一个background的模式的configuration
let configuration = URLSessionConfiguration.background(withIdentifier: "com.spirej.zeYaoTechnology")
// 2:通过configuration初始化网络下载会话
let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
// 3:session创建downloadTask任务-resume启动
session.downloadTask(with: url).resume()
//MARK: - session代理
extension ViewController:URLSessionDownloadDelegate{
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        // 下载完成 - 开始沙盒迁移
        print("下载完成 - \(location)")
        let locationPath = location.path
        //拷贝到用户目录(文件名以时间戳命名)
        let documnets = NSHomeDirectory() + "/Documents/" + self.lgCurrentDataTurnString() + ".mp4"
        print("移动地址:\(documnets)")
        //创建文件管理器
        let fileManager = FileManager.default
        try! fileManager.moveItem(atPath: locationPath, toPath: documnets)
    }
    
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        print(" bytesWritten \(bytesWritten)\n totalBytesWritten \(totalBytesWritten)\n totalBytesExpectedToWrite \(totalBytesExpectedToWrite)")
        print("下载进度: \(Double(totalBytesWritten)/Double(totalBytesExpectedToWrite))\n")
    }
}

这里实现了下载功能,但是对于后台下载还差一段

AppDelegate类里面设置后台下载的handleEventsForBackgroundURLSession事件,告诉URLSession相关事件正在后台处理

class AppDelegate: UIResponder, UIApplicationDelegate {
    //用于保存后台下载的completionHandler
    var backgroundSessionCompletionHandler: (() -> Void)?
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
         self.backgroundSessionCompletionHandler = completionHandler
    }
}

urlSessionDidFinishEvents的代理实现调用

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

那么如果不实现这个代理里面的回调函数的执行,那么会发生什么呢

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

二(1). 先说URLSession是如何处理后台下载

Alamofire用起来那叫一个倍儿爽!函数式回调,链式请求和响应,事物逻辑非常清晰,简洁明了。

ZYBackgroundManger.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)")
}

为什么要做成单例?

struct ZYBackgroundManger {    
    static let shared = ZYBackgroundManger()

    let manager: SessionManager = {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.zeYao.AlamofireTest.demo")
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
        configuration.timeoutIntervalForRequest = 10
        configuration.timeoutIntervalForResource = 10
        configuration.sharedContainerIdentifier = "group.com.zeYao.AlamofireTest"
        return SessionManager(configuration: configuration)
    }()
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    ZYBackgroundManger.shared.manager.backgroundCompletionHandler = completionHandler
}
/// The background completion handler closure provided by the UIApplicationDelegate
/// `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background
/// completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation
/// will automatically call the handler.
///
/// If you need to handle your own events before the handler is called, then you need to override the
/// SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished.
///
/// `nil` by default.
open var backgroundCompletionHandler: (() -> Void)?

三、SessionManager流程分析

SessionManager主要干了两件事:

1. SessionManger初始化

SessionManger两个初始化方法唯一不同的地方就是一个是可选类型的init?

初始化了session,其中configurationdefault的模式,设置了一些基本的 SessionManager.defaultHTTPHeaders请求头信息

2. 代理移交

SessionManger初始化时把处理下载的代理方法移交给SessionDelegate类,SessionDelegate集合了所有的代理。

这里我们根据需求来到 urlSessionDidFinishEvents 的代理

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

在我们的 SessionManger 里面的初始化的时候,有一个方法commonInit

delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
    guard let strongSelf = self else { return }
    DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
3. 流程总结
上一篇下一篇

猜你喜欢

热点阅读