Alamofire (2)—— 后臺下載

??????Alamofire專題目錄,歡迎及時反饋交流 ??????


Alamofire 目錄直通車 --- 和諧學習,不急不躁!


這一篇主要講解后臺下載齿兔,后臺下載對于應用程序來說拍埠,是一個非常重要也比較好用的功能。雖然用好后臺下載的確能夠大大提升用戶體驗门扇,但是又很多時候我們也會遇到很多坑點以及疑惑點斗锭。其中會通過 URLSessionAlamofire 兩種形式分別展開討論地淀,對比學習才能更能體會 Alamofire 的設計思維。Alamofire持續(xù)更新中岖是,希望大家希望帮毁!

一、URLSession處理后臺下載

URLSession在后臺處理方面還是比較簡單的豺撑。

// 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創(chuàng)建downloadTask任務-resume啟動
session.downloadTask(with: url).resume()
  • 初始化一個 background 的模式的 configuration烈疚。configuration 三種模式 ,只有background 的模式才能進行后臺下載。
  • 通過configuration初始化網絡下載會話 session,設置相關代理聪轿,回調數據信號響應爷肝。
  • session創(chuàng)建downloadTask任務-resume啟動 (默認狀態(tài):suspend)
  • 接下來依賴蘋果封裝的網絡處理,發(fā)起連接 - 發(fā)送相關請求 - 回調代理響應
//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)")
        //創(chuàng)建文件管理器
        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")
    }
}
  • 實現了 URLSessionDownloadDelegatedidFinishDownloadingTo代理陆错,實現下載完成轉移臨時文件里的數據到相應沙盒保存
  • 通過urlSession(_ session: downloadTask:didWriteData bytesWritten: totalBytesWritten: totalBytesExpectedToWrite: ) 的代理監(jiān)聽下載進度
  • 這里也是因為 http的分片傳輸 才導致的進度有段的感覺,其實證明內部也是對這個代理方法不斷調用灯抛,才能進度回調!

這里實現了下載功能危号,但是對于我們需要的后臺下載還差一段

Applications using an NSURLSession with a background configuration may be launched or resumed in the background in order to handle the completion of tasks in that session, or to handle authentication. This method will be called with the identifier of the session needing attention. Once a session has been created from a configuration object with that identifier, the session's delegate will begin receiving callbacks. If such a session has already been created (if the app is being resumed, for instance), then the delegate will start receiving callbacks without any action by the application. You should call the completionHandler as soon as you're finished handling the callbacks.

蘋果爸爸總是能在合適時間給你優(yōu)秀的建議牧愁,閱讀文檔的能力決定你是否能夠在這個時代站穩(wěn)自己的腳尖

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就可以完美后臺下載
  • 告訴代理與 URLSession 相關的事件正在等待處理素邪。
  • 應用程序在所有與 URLSession對象 關聯的后臺傳輸完成后調用此方法外莲,無論傳輸成功完成還是導致錯誤。如果一個或多個傳輸需要認證兔朦,應用程序也會調用這個方法偷线。
  • 使用此方法可以重新連接任何 URLSession 并更新應用程序的用戶界面。例如沽甥,您可以使用此方法更新進度指示器或將新內容合并到視圖中声邦。在處理事件之后,在 completionHandler 參數中執(zhí)行 block摆舟,這樣應用程序就可以獲取用戶界面的刷新亥曹。
  • 我們通過handleEventsForBackgroundURLSession保存相應的回調邓了,這也是非常必要的!告訴系統(tǒng)后臺下載回來及時刷新屏幕

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()
    }
}
  • 拿到UIApplication.shared.delegate的回調函數執(zhí)行
  • 注意線程切換主線程媳瞪,畢竟刷新界面

那么如果不實現這個代理里面的回調函數的執(zhí)行骗炉,那么會發(fā)生什么呢

  • 后臺下載的能力是不會影響的
  • 但是會爆出非常驗證界面刷新卡頓,影響用戶體驗
  • 同時打印臺會爆出警告
Warning: Application delegate received call to -
application:handleEventsForBackgroundURLSession:completionHandler: 
but the completion handler was never called.

二蛇受、Alamofire后臺下載

Alamofire框架還是比較有感覺的句葵,這個節(jié)奏也是函數式回調,還支持鏈式請求和響應兢仰!事務邏輯非常清晰乍丈,還有代碼可讀性也是非常簡潔

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)")
}
  • 這里封裝了一個單利LGBackgroundManger的后臺下載管理類,調用manger的手法也是非常直接把将。
  • 封裝的思想再也不需要去處理惡心的代理事件
struct LGBackgroundManger {    
    static let shared = LGBackgroundManger()

    let manager: SessionManager = {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.lgcooci.AlamofireTest.demo")
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
        configuration.timeoutIntervalForRequest = 10
        configuration.timeoutIntervalForResource = 10
        configuration.sharedContainerIdentifier = "group.com.lgcooci.AlamofireTest"
        return SessionManager(configuration: configuration)
    }()
}

可能很多同學都在質疑為什么要做成單利轻专,URLSession的時候不是挺好的?

  • 如果你是 SessionManager.defalut 顯然是不可以的察蹲!畢竟要求后臺下載铭若,那么我們的會話 session 的配置 URLSessionConfiguration 是要求 background模式的
  • 如果你配置出來不做成單利,或者不被持有递览!在進入后臺就會釋放叼屠,網絡也就會報錯:Error Domain=NSURLErrorDomain Code=-999 "cancelled"
  • 應用層與網絡層也可以達到分離。
  • 能夠幫助在AppDelegate 的回調方便直接接收
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    LGBackgroundManger.shared.manager.backgroundCompletionHandler = completionHandler
}

三绞铃、SessionManger流程分析

一篇優(yōu)秀的博客镜雨,畢竟還要跟大家交代這樣清晰的代碼的背后流程

1、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)
}
  • 初始化了session,其中configurationdefault的模式儿捧,設置了一些基本的 SessionManager.defaultHTTPHeaders 請求頭信息
  • 代理移交荚坞,通過創(chuàng)建 SessionDelegate 這個專門處理代理的類來實現 URLSession的代理
2、代理完成回調

SessionDelegate 是一個非常重要的類菲盾,集合所有的代理

  • URLSessionDelegate
  • URLSessionTaskDelegate
  • URLSessionDataDelegate
  • URLSessionDownloadDelegate
  • URLSessionStreamDelegate

這里我們根據需求來到 urlSessionDidFinishEvents 的代理

open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    sessionDidFinishEventsForBackgroundURLSession?(session)
}
  • 這里執(zhí)行了 sessionDidFinishEventsForBackgroundURLSession 閉包的執(zhí)行颓影,那么這個閉包在什么時候申明的呢?
  • 如果你足夠聰明懒鉴,這里你應該是能夠想到的诡挂,SessionDelegate只是處理代理的專門類,但不是邏輯數據的處理類临谱,按照封裝設計的常規(guī)思路必將交給管理者類來下發(fā)

在我們的 SessionManger 里面的初始化的時候璃俗,有一個方法commonInit

delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
    guard let strongSelf = self else { return }
    DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
}
  • 這里就是代理的delegate.sessionDidFinishEventsForBackgroundURLSession閉包的聲明
  • 只要后臺下載完成就會來到這個閉包內部
  • 回調了主線程,調用了 backgroundCompletionHandler , 這也是 SessionManger 對外提供的功能悉默!聰明的你應該知道知道了我在application的操作的本質了城豁!

3、流程總結

  • 首先在 AppDelegatehandleEventsForBackgroundURLSession方法里抄课,把回調閉包傳給了 SessionManagerbackgroundCompletionHandler
  • 在下載完成回來的時候 SessionDelegateurlSessionDidFinishEvents代理的調用 -> sessionDidFinishEventsForBackgroundURLSession 調用
  • 然后sessionDidFinishEventsForBackgroundURLSession 執(zhí)行 -> SessionManagerbackgroundCompletionHandler的執(zhí)行
  • 最后導致 AppDelegatecompletionHandler 的調用

無論你是使用 URLSession 的方式唱星,還是 Alamofire 進行后臺下載雳旅,但是原理還是一樣的,只是 Alamofire 使用更加達到依賴下沉间聊,網絡層下沉岭辣,使用更簡潔,這也是很多時候我們需要第三方框架的原因甸饱。這一篇你估計已經感受到了 Alamofire 的舒服沦童,那么如果你喜歡的話,麻煩點心叹话,關注一下偷遗。我會持續(xù)更新一個 Alamofire 的系列專題,謝謝驼壶!

就問此時此刻還有誰氏豌?45度仰望天空,該死热凹!我這無處安放的魅力泵喘!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市般妙,隨后出現的幾起案子纪铺,更是在濱河造成了極大的恐慌,老刑警劉巖碟渺,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鲜锚,死亡現場離奇詭異,居然都是意外死亡苫拍,警方通過查閱死者的電腦和手機芜繁,發(fā)現死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绒极,“玉大人骏令,你說我怎么就攤上這事÷⑻幔” “怎么了榔袋?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長塔淤。 經常有香客問我摘昌,道長,這世上最難降的妖魔是什么高蜂? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮罕容,結果婚禮上备恤,老公的妹妹穿的比我還像新娘稿饰。我一直安慰自己,他們只是感情好露泊,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布喉镰。 她就那樣靜靜地躺著,像睡著了一般惭笑。 火紅的嫁衣襯著肌膚如雪侣姆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天沉噩,我揣著相機與錄音捺宗,去河邊找鬼。 笑死川蒙,一個胖子當著我的面吹牛蚜厉,可吹牛的內容都是我干的。 我是一名探鬼主播畜眨,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼昼牛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了康聂?” 一聲冷哼從身側響起贰健,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恬汁,沒想到半個月后霎烙,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蕊连,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年悬垃,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甘苍。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡尝蠕,死狀恐怖,靈堂內的尸體忽然破棺而出载庭,到底是詐尸還是另有隱情看彼,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布囚聚,位于F島的核電站靖榕,受9級特大地震影響,放射性物質發(fā)生泄漏顽铸。R本人自食惡果不足惜茁计,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谓松。 院中可真熱鬧星压,春花似錦践剂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至竣贪,卻和暖如春军洼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背演怎。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工匕争, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颤枪。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓汗捡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親畏纲。 傳聞我的和親對象是個殘疾皇子扇住,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345