美文网首页
Alamofire实现后台下载

Alamofire实现后台下载

作者: H_Zimi | 来源:发表于2019-08-18 21:28 被阅读0次

    咔咔咔,敲完一个Alamofire的下载实现:

    func downLoadFile() {
        SessionManager.default.download(urlString) { (url, response) -> (destinationURL: URL, options: DownloadRequest.DownloadOptions) in
            let docUrl = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first
            let fileUrl = docUrl?.appendingPathComponent(response.suggestedFilename!)
            return (fileUrl!, [.removePreviousFile, .createIntermediateDirectories])
            }.downloadProgress { (progress) in
                print("\(progress)")
            }.response { (respond) in
                print("\(respond)")
        }
    }
    

    切到后台时,下载不继续执行,切回后,下载继续执行,后台下载的目的没有达到啊。。。
    一通常规操作,目的没有达到啊,为什么?肯定哪里忽略了,default有木有很刺眼?看下呗

    public static let `default`: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
        
        return SessionManager(configuration: configuration)
    }()
    
    • SessionManager的一个单例
    • URLSessionConfigurationdefault模式
    • 后台下载需要的模式是background

    再看下SessionManagerinit方法

    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的默认值还是URLSessionConfigurationdefualt模式

    这个时候我们就需要重新配置为background模式了:

    func downLoadBackground() {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.zimi")
        let backgroundManager = SessionManager(configuration: configuration)
        backgroundManager.download(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!, [.createIntermediateDirectories, .removePreviousFile])
        }
            .response { (response) in
                print("\(response)")
            }.downloadProgress { (progress) in
                print("\(progress.fractionCompleted)")
        }
    }
    // 控制台打印:<1> load failed with error Error Domain=NSURLErrorDomain Code=-999 "cancelled"
    

    居然报错了。。。
    原来是SessionManagerdownLoadBackground方法中是局部变量,进入后台下载时被释放了
    改成这样
    let backgroundManager: SessionManager = {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.zimi")
        let sessionManager = SessionManager(configuration: configuration)
        
        return sessionManager
    }()
    

    URLSession的官方文档关于后台下载有四步,在Swift - 网络 URLSession中有介绍,当然不能忘了这个重要的步骤了:

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

    不然在控制台会打印一个警告,切回的时候也会出下卡顿

    Warning: Application delegate received call to -application:handleEventsForBackgroundURLSession:completionHandler: 
    but the completion handler was never called.
    

    那么重点来了,在用URLSession来处理后台下载的时候,需要通过urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession)代理方法来执行completionHandler的回调,那么,既然SessionManager的属性backgroundCompletionHandler帮我们保存了completionHandler这个闭包,它是怎么帮我们来调用的呢?
    在前面贴出的init方法中有commonInit这个方法的调用,那么我们来看下:

    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?() }
        }
    }
    
    • sessionDidFinishEventsForBackgroundURLSession代理的闭包声明里,做了backgroundCompletionHandler闭包回到主线程异步的回调
    • Alamofire中有一个专职delegate的类SessionDelegate,对URLSession的代理方法urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession)进行了实现
    open func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        sessionDidFinishEventsForBackgroundURLSession?(session)
    }
    
    • SessionDelegate重写了NSObjectresponds方法,通过sessionDidFinishEventsForBackgroundURLSession闭包是否为空来判断是否执行urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession)方法
    open override func responds(to selector: Selector) -> Bool {
        #if !os(macOS)
            if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
                return sessionDidFinishEventsForBackgroundURLSession != nil
            }
        #endif
       //省略了一些代码
    }
    

    是不是很6啊?不用我们再写代理,也不用再写代理方法的实现了,Alamofire帮我们省了这一步了。

    Alamofire这些优秀的框架能帮我们省很多的代码,但我们也不能忘了原生API的基础哦

    相关文章

      网友评论

          本文标题:Alamofire实现后台下载

          本文链接:https://www.haomeiwen.com/subject/hvsrsctx.html