Alamofire 后台下载和流程分析

2019-08-21  本文已影响0人  盾子

前言

在开发中,为了提升用户体验常常会把下载大文件等网络请求放到后台下载,这样即使用户进入了后台,任务也能继续进行。那么这篇文章就来讨论下如何使用Apple原生框架URLSession的API和基于URLSession的第三方框架Alamofire来实现后台下载功能。

通过URLSession实现后台下载

一、 首先发起一个background模式的请求:

// 初始化一个background的模式的configuration. Identifier:配置对象的唯一标识符
let configuration = URLSessionConfiguration.background(withIdentifier: "com.test")
// 创建session
let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
// 创建downloadTask任务,然后resume启动
session.downloadTask(with: URL(string: urlStr).resume()

二、 实现相关代理回调

extension ViewController:URLSessionDownloadDelegate {
    // 下载完成回调
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        let locationPath = location.path
        let documnets = NSHomeDirectory() + "/Documents/" + "\(Date().timeIntervalSince1970)" + ".mp4"
        let fileManager = FileManager.default
        //拷贝到用户目录
        try! fileManager.moveItem(atPath: locationPath, toPath: documnets)
    }
    
    // 监听下载进度。http分片段传输,所以这个代理会回调多次
    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中处理后台下载回调闭包

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    //用于保存后台下载的completionHandler
    var backgroundSessionCompletionHandler: (() -> Void)?
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        self.backgroundSessionCompletionHandler = completionHandler
    }
}
extension ViewController:URLSessionDownloadDelegate {
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        print("后台任务下载回来")
        DispatchQueue.main.async {
            guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
            backgroundHandle()
        }
    }
}

通过Alamofire实现后台下载

一、创建一个下载任务

BackgroundManger.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 BackgroundManger {
    static let shared = BackgroundManger()
    let manager: SessionManager = {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.text.demo")
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
        return SessionManager(configuration: configuration)
    }()
}

二、处理AppDelegate中的回调

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    BackgroundManger.shared.manager.backgroundCompletionHandler = completionHandler
}

三、SessionManger流程分析
Alamofire使用起来非常简单,那么它内部是怎样帮我们处理一些繁琐的事情的呢,下面一起来分析下:

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)
}
open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    sessionDidFinishEventsForBackgroundURLSession?(session)
}
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?() }
    }
}

总结

Alamofire是对URLSession进行封装,所以这两种方式进行后台下载,原理是一样的。只是 Alamofire 使用更加简洁方便,依赖下沉,网络层下沉。在自己写sdk或者项目的时候也可以参照这种设计思想去实现。

有问题或者建议和意见,欢迎大家评论或者私信。
喜欢的朋友可以点下关注和喜欢,后续会持续更新文章。

上一篇下一篇

猜你喜欢

热点阅读