Alamofire(三)后台下载原理

2,797 阅读4分钟

Alamofire学习(一)网络基础

Alamofire(二)URLSession

Alamofire(三)后台下载原理

@TOC

  • 前面两篇博客:alamofire(一)网络基础alamofire(二) URLSession 分别讲解了alamofire框架实现所需的一些必备的网络基础知识和iOS 系统网络框架Api。属性这些知识后,我们再来探索alamofire框架的设计思想和底层实现就事半功倍了。本篇博客主要讲解Alamofire的一个重要功能“后台下载”的实现原理。
  • 我们先来回顾一下alamofire(一)网络基础这篇博客留下的一个彩蛋:

alamofire框架简介
从这个结构图里面我们可以大致了解一下Alamofire框架主要涉及哪些方面的知识。总的来说alamofire是一个非常牛逼的网络框架,它是AFNetwork的父亲开发的,在github上面有3万多个星星,足以证明他是多么的牛B。非常值得我们深入研究。

URLSession后台下载

  • 在上一篇博客 alamofire(二) URLSession 已经大致的讲解了一些URLSession后台下载的流程和实现代码。这里再来回顾总结一下,有利于更好的理解Alamofire框架后台下载的原理。

URLSession后台流程

URLSession实现后台下载的步骤如下图:

URLSession实现后台下载的步骤
如上图6步就可以实现后台下载,其实代码实现起来也很简单。

  • 上图的1-3步可以由于代码比较简单可以合在一起讲,具体就是:首先初始化一个 background 的模式的 configuration。configuration有 三种模式 ,只有background 的模式才能进行后台下载。 然后,通过configuration初始化网络下载会话 session,设置相关代理,回调数据信号响应。 最后,session创建downloadTask任务-resume启动 (默认状态:suspend),具体代码如下:
// 1:初始化一个background的模式的configuration
let configuration = URLSessionConfiguration.background(withIdentifier: self.createID())
// 2:通过configuration初始化网络下载会话
let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
// 3:session创建downloadTask任务-resume启动
let url = URL(string: "https://dldir1.qq.com/qqfile/QQforMac/QQ_V6.5.5.dmg")!
session.downloadTask(with: url).resume()
  • 接下来就是第4步,设置代理。依赖苹果封装的网络处理API,发起连接 - 发送相关请求 - 回调代理响应 这里只需要实现两个回调就可以了
//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")
    }
}
  • 接着下需要处理第5步中的backgroundSessionCompletionHandler回调。定义一个全局变量用于保存后台下载的completionHandler
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    //用于保存后台下载的completionHandler
    var backgroundSessionCompletionHandler: (() -> Void)?
    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
        self.backgroundSessionCompletionHandler = completionHandler
    }
}

注意:我们通过handleEventsForBackgroundURLSession保存相应的回调,这也是非常必要的!告诉系统后台下载回来及时刷新屏幕

  • 最后第6步:在urlSessionDidFinishEvents的代理实现调用。代码如下:
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    print("后台任务下载回来")
    //因为涉及到UI刷新,需要确保在主线程中进行
    //如果不执行下面代码,会导致刚进入前台时页面卡顿,影响用户体验,同时还会打印警告信息。
    DispatchQueue.main.async {
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
        backgroundHandle()
    }
}

注意:如果不实现第6步这个代理里面的回调函数的执行,系统会打印警告信息:`Warning: Application delegate received call to - application:handleEventsForBackgroundURLSession:completionHandler: but the completion handler was never called. 虽然这样还是可以实现后台下载,但是会导致总在IOS系统总在后台刷新,导致性能浪费,出现页面卡顿,影响用户体验。

  • 完成如上6个步骤就可以实现后台下载功能.保存handleEventsForBackgroundURLSession方法的completionHandler回调,这是非常重要的。告诉系统后台下载回来及时刷新屏幕.
  • 在切到后台之后,URLSessionDownloadDelegate 的代理方法不会再收到 Task 相关的消息。当所有 Task 全都完成后,系统才会调用 AppDelegate 的application:handleEventsForBackgroundURLSession:completionHandler:回调。

Alamofire后台下载

在讲解Alamofire后台下载前,有必要先熟悉一下Alamofire下载需要使用的几个关键类。

SessionManager

参考:大神Cooci博客:juejin.cn/post/684490…