@TOC
- 前面两篇博客:alamofire(一)网络基础, alamofire(二) URLSession 分别讲解了alamofire框架实现所需的一些必备的网络基础知识和iOS 系统网络框架Api。属性这些知识后,我们再来探索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博客:https://juejin.im/post/5d57ff89f265da03913512df
网友评论