Alamofire ——后臺(tái)下載

我們先從基本的URLSession后臺(tái)下載入手兵琳,對比看下Alamofire的后臺(tái)下載

URLSession后臺(tái)下載

///創(chuàng)建一個(gè)后臺(tái)下載的configuration
let configuration = URLSessionConfiguration.background(withIdentifier: self.createID())
///使用configuration初始化URLSession
let session = URLSession.init(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
///創(chuàng)建task任務(wù) 千萬別忘了resume開啟!       
session.downloadTask(with: url).resume()

URLSessionConfiguration有三種模式罐氨,后臺(tái)下載用到的是background,另外兩種是defaultephemeral滩援。顧名思義栅隐,平時(shí)用的就是default, 默認(rèn)的URL會(huì)話配置,其存儲(chǔ)方式是基于硬盤的持久化存儲(chǔ)方式玩徊,會(huì)保存用戶的證書到鑰匙串中租悄。ephemeral對緩存、cookie或證書不使用持久存儲(chǔ)恩袱。

接下來就是URLSessionDownloadDelegate代理的實(shí)現(xiàn)

//MARK: - session代理
extension ViewController: URLSessionDownloadDelegate{
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        // 下載完成 - 開始沙盒遷移
        print("下載完成 - \(location)")
        let locationPath = location.path
        //拷貝到用戶目錄(文件名以時(shí)間戳命名)
        let documnets = NSHomeDirectory() + "/Documents/" + self.lgCurrentDataTurnString() + ".mp4"
        print("移動(dòng)地址:\(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("下載進(jìn)度: \(Double(totalBytesWritten)/Double(totalBytesExpectedToWrite))\n")
    }
}
  • didFinishDownloadingTo在下載完成的時(shí)候會(huì)調(diào)用泣棋,一般下載的文件都存儲(chǔ)在臨時(shí)文件里面,我們要把文件保存到相應(yīng)的沙盒路徑就在這里操作畔塔。
  • didWriteData這個(gè)代理方法監(jiān)聽下載進(jìn)度潭辈。

到這里并沒有完成后臺(tái)下載所有步驟,翻閱官方文檔可知俩檬,這里還需要實(shí)現(xiàn)一個(gè)APPDelegate中處理后臺(tái)下載的代理方法handleEventsForBackgroundURLSession

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

URLSessionDownloadDelegate也要實(shí)現(xiàn)urlSessionDidFinishEvents代理方法萎胰,這樣才可以完整的實(shí)現(xiàn)后臺(tái)下載
注意切換主線程,UI刷新是要回來滴

    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        print("后臺(tái)任務(wù)下載回來")
        DispatchQueue.main.async {
            guard let appDelegate = UIApplication.shared.delegate as? AppDelegate, let backgroundHandle = appDelegate.backgroundSessionCompletionHandler else { return }
            backgroundHandle()
        }
    }

Alamofire后臺(tái)下載

首先要用單利封裝一下Alamofire的后臺(tái)下載管理類棚辽,要考慮后臺(tái)下載作用域的問題技竟,上面URLSession的例子就可以看出,必然要在APPDelegate里面實(shí)現(xiàn)代理方法的屈藐。而且Session不被持有的話榔组,當(dāng)進(jìn)入后臺(tái)的時(shí)候就被釋放了,無法進(jìn)行回調(diào)联逻。

單利封裝Alamofire后臺(tái)下載管理類

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

不需要實(shí)現(xiàn)下載回調(diào)的代理搓扯,直接鏈?zhǔn)秸{(diào)用

AlamofireBackgroundManger.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("下載回調(diào)信息: \(downloadResponse)")
    }
    .downloadProgress { (progress) in
                print("下載進(jìn)度 : \(progress)")
    }

APPDelegate里面直接用單利接收

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

Alamofire的SessionManager源碼分析

1、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)
    }
  • configuration默認(rèn)是default包归,配置了一些請求頭的基本信息锨推,感興趣可以抓包看看
  • delegate代理移交,創(chuàng)建一個(gè)SessionDelegate()URLSession的代理移交給自己實(shí)現(xiàn)
  • commonInit做了什么下面講

2公壤、代理
源碼點(diǎn)進(jìn)去看 SessionDelegate這個(gè)類换可,它集合了所有URLSession的代理

  • URLSessionDelegate
  • URLSessionTaskDelegate
  • URLSessionDataDelegate
  • URLSessionDownloadDelegate
  • URLSessionStreamDelegate

還記得urlSessionDidFinishEvents這個(gè)回調(diào)嗎,進(jìn)去看看

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

sessionDidFinishEventsForBackgroundURLSession這個(gè)閉包是在哪里聲明的厦幅?代理類里面是不會(huì)有邏輯數(shù)據(jù)處理的沾鳄,封裝設(shè)計(jì)的思路必將這種處理交給管理類下發(fā)

初始化的時(shí)候有個(gè)commonInit方法是不是還沒有看?現(xiàn)在去看看

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

這里有delegate.sessionDidFinishEventsForBackgroundURLSession的聲明确憨,只要下載完成就會(huì)來到這個(gè)閉包內(nèi)部译荞,回到主線程調(diào)用SessionManager對外提供的.backgroundCompletionHandler閉包瓤的,在APPDelegate中的代理方法handleEventsForBackgroundURLSession就是把回調(diào)傳給這個(gè)閉包。

流程總結(jié)圖

SessionManager后臺(tái)下載流程圖.png

使用Alamofire跟URLSession進(jìn)行網(wǎng)絡(luò)請求的原理是一樣的吞歼,但是Alamofire的封裝使依賴和網(wǎng)絡(luò)層下沉圈膏,使用鏈?zhǔn)秸埱螅瘮?shù)式回調(diào)讓代碼簡潔可讀性更高

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末篙骡,一起剝皮案震驚了整個(gè)濱河市本辐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌医增,老刑警劉巖慎皱,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異叶骨,居然都是意外死亡茫多,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門忽刽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來天揖,“玉大人,你說我怎么就攤上這事跪帝〗癫玻” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵伞剑,是天一觀的道長斑唬。 經(jīng)常有香客問我,道長黎泣,這世上最難降的妖魔是什么恕刘? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮抒倚,結(jié)果婚禮上褐着,老公的妹妹穿的比我還像新娘。我一直安慰自己托呕,他們只是感情好含蓉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著项郊,像睡著了一般馅扣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呆抑,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天岂嗓,我揣著相機(jī)與錄音汁展,去河邊找鬼鹊碍。 笑死厌殉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侈咕。 我是一名探鬼主播公罕,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耀销!你這毒婦竟也來了楼眷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對情侶失蹤熊尉,失蹤者是張志新(化名)和其女友劉穎罐柳,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狰住,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡张吉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了催植。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肮蛹。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖创南,靈堂內(nèi)的尸體忽然破棺而出伦忠,到底是詐尸還是另有隱情,我是刑警寧澤稿辙,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布昆码,位于F島的核電站,受9級(jí)特大地震影響邻储,放射性物質(zhì)發(fā)生泄漏未桥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一芥备、第九天 我趴在偏房一處隱蔽的房頂上張望冬耿。 院中可真熱鬧,春花似錦萌壳、人聲如沸亦镶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缤骨。三九已至,卻和暖如春尺借,著一層夾襖步出監(jiān)牢的瞬間绊起,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工燎斩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虱歪,地道東北人蜂绎。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像笋鄙,于是被迫代替她去往敵國和親师枣。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容