设备休眠时的 Alamofire 请求

Posted

技术标签:

【中文标题】设备休眠时的 Alamofire 请求【英文标题】:Alamofire requests while device is sleeping 【发布时间】:2020-06-26 17:52:12 【问题描述】:

我的 ios 应用从我们的主服务接收到刷新其状态的通知。现在,我们正在使用 Alamofire 获取最新状态,并在有更新数据时播放持续声音。我们的设备被锁定在引导模式,以防止它们关闭并提供信息亭体验。

我们正在进行更改,以便设备可以在 xx 分钟不活动后进入睡眠状态。但是,我们遇到了一个问题,即即使请求已成功发送(基于我们在 api 端的日志),设备也没有从 Alamofire 获取结果。

当我使用 Alamofire 4 时,我设置了一个带有 backgroundsessionmanager 的单例,这就是现在发送 AF 请求的方式。但挑战在于,请求是间歇性发送的,并且大部分时间在设备因此错误而处于睡眠状态时都会失败:

Domain=NSURLErrorDomain Code=-997 "Lost connection to background transfer service"

这是我的单例代码(我在 AppDelegate 中有相关代码):

class Networking 
    static let sharedInstance = Networking()
    public var sessionManager: Alamofire.SessionManager // most of your web service clients will call through sessionManager
    public var backgroundSessionManager: Alamofire.SessionManager // your web services you intend to keep running when the system backgrounds your app will use this
    private init() 
        self.sessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.default)
        self.backgroundSessionManager = Alamofire.SessionManager(configuration: URLSessionConfiguration.background(withIdentifier: "com.test.app"))
    

这是我发送请求的代码:

let NetworkManager = Networking.sharedInstance.backgroundSessionManager

        
        DispatchQueue.main.async(execute: 
            NetworkManager.request(self.api_url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON response in
            
                switch (response.result)
                    case .success:
                        if let jsonResp = response.result.value
                            print("got the response")
                            print(jsonResp)
                            // parse results
                        
                    
                    case .failure:
                        print("Network Error: \(response.error)")
                    
                
            
        )

我希望得到一些帮助来解决这个问题,因为我无法从根本上导致不一致的行为。我在某些地方读到 Apple/iOS 只允许在应用程序处于后台模式而不是 requests 时上传/下载。

【问题讨论】:

与您的问题无关,DispatchQueue.main.async 可能不需要。 Alamofire 请求已经异步运行。如果您的应用程序在后台运行(作为后台获取的结果),这并不意味着它在后台线程上运行。 “后台”一词有三种完全不同的用法:后台队列/线程(相对于主队列/线程);后台模式(而不是在前台运行);和后台、进程外、URL 会话(而不是默认、进程中、会话)。但这是三个完全不同的概念。 顺便说一句,按照惯例,变量和属性名称通常应以小写字母开头。因此,您可以使用let networkManager = ... 而不是NetworkManager。这意味着,一目了然,您可以轻松地区分变量名和类型名。 感谢您指出改进。我会做出调整。 【参考方案1】:

是的,后台会话只允许上传和下载任务,不允许数据任务。它们也只允许基于委托的请求,不允许基于完成处理程序的请求。 This answer 概述了与 Alamofire 结合使用时的许多注意事项。

但这引出了一个问题,即您是否真的想使用后台会话。当您的应用程序被唤醒以进行后台获取时,如果您能够在合理的时间内(例如 30 秒)完成您的请求,您可能应该考虑使用标准会话,而不是后台会话。这要简单得多。

不要将在“后台”中运行的应用与“后台”URLSessionConfiguration 混为一谈:它们是完全不同的模式。仅仅因为您的应用程序在后台运行,并不意味着您必须使用后台URLSessionConfiguration。如果您的应用程序正在运行(无论是在前台还是在后台),那么标准会话就可以了。如果您希望在应用程序暂停(或终止)后继续使用后台会话,并且您愿意承担后台会话带来的所有额外开销,则您只需要后台会话。

后台会话不适用于应用在后台运行时执行的请求。它们适用于在您的应用程序暂停后将继续的请求(即使它最终在其自然生命周期过程中终止)。这意味着后台会话非常适合无法在合理时间内完成的缓慢请求,例如下载视频资产、下载许多大型图像资产或文档等。

但是,如果您只是执行将在合理的时间内完成的例行 GET/POST 请求,请考虑不要使用后台 URLSessionConfiguration,而只需执行正常请求并在您的请求完成时调用后台获取完成处理程序完成(即在您的网络请求的完成处理程序中)。

【讨论】:

谢谢你的详细解释,Rob。我的请求/响应通常需要不到 5 秒的时间,所以很高兴知道我可以使用标准会话。您是否介意帮助我理解为什么 Alamofire 请求也无法通过标准会话完成。例如,我看到请求发送到我们的 API,但响应没有得到处理。 @whawhat - 当系统唤醒我们的应用程序在后台运行时,它通常会为我们提供一个完成处理程序来调用它以告知它何时完成。那应该进入这个完成处理程序。您可能会在请求有机会完成之前调用它吗? 感谢您的快速响应。现在,当通知到达时,我正在推送到 notificationCenter 并且有一个观察者从触发请求的类中抓取。你的意思是我应该将AF请求中的完成处理程序添加到didReceiveRemoteNotification:fetchCompletionHandler的完成处理程序中吗?我还在我的 plist 文件中设置了 UIApplicationExitsOnSuspend 键。这会导致一些问题吗? @whawhat - 是的,我是说你想从你的网络请求完成处理程序内部调用提供的fetchCompletionHandler,而不是之前。提供的完成处理程序的全部目的是让操作系统知道您何时完成所有操作并且应用程序可以再次暂停/终止。但在网络请求完成之前,您不希望这种情况发生。 谢谢。我想我已经足够从这里出发了。如果我有更多问题,我会回复。再次感谢您的所有帮助!

以上是关于设备休眠时的 Alamofire 请求的主要内容,如果未能解决你的问题,请参考以下文章

Alamofire 请求大约需要 10 秒

错误:导入 Alamofire

iOS Swift 2 - 失败时的 Alamofire 打印响应值

在 Alamofire 4.0 中初始化 SessionManager

安装库 Alamofire 可能出现错误

Alamofire:在特定 URL 和设备上请求超时