【问题标题】:How can I start backgroundly URLSession in iOS?如何在 iOS 中启动后台 URLSession?
【发布时间】:2016-10-12 21:22:35
【问题描述】:

请告诉我如何在后台开始URLSession。我只找到了如何完成和处理结果的方法。接下来是逻辑:静默推送通知命令向 Web 服务器执行一些请求并获取一些 json,然后执行其他任务。 不要建议后台获取,因为我需要每隔几分钟刷新一次数据。

编辑:

所以,实际上我的应用程序只打印 connected startMonitoring connected,没有别的。我想最终收到 JSON 收到

很抱歉没有提供我的代码。这里是:

AppDelegate.swift:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    let aps = userInfo["aps"] as! [String: AnyObject]
    print("connected")
    // 1
    if (aps["content-available"] as? NSString)?.integerValue == 1 {
        var MLController = ((window?.rootViewController as? UITabBarController)?.viewControllers?.first as? UINavigationController)?.viewControllers.first as? MonitoringListTableViewController
        MLController?.startMonitoring()
        //completionHandler(UIBackgroundFetchResult.noData)
        print("connected")
    }
}

MonitoringListTableViewController.swift

    func startMonitoring() {

        print("startMonitoring")
        self.connect()
        //tokenTimer.invalidate()
        //tokenTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(tokenReloader), userInfo: nil, repeats: true)

    }

    func connect() {

        //Gets encrypted token

        for theCookie in HTTPCookieStorage.shared.cookies! {
            HTTPCookieStorage.shared.deleteCookie(theCookie)
        }
        let request = NSMutableURLRequest(url: URL(string: "http://...")!)

        let task = URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
            guard error == nil && data != nil else {           // check for fundamental networking error
                print("error=\(error)")
                return
            }
            if let httpStatus = response as? HTTPURLResponse , httpStatus.statusCode != 200 {           // check for http errors
                print("statusCode should be 200, but is \(httpStatus.statusCode)")
                print("response = \(response)")
            }

            let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
            //print("responseString = \(response), \(responseString)")
            for theCookie in HTTPCookieStorage.shared.cookies! {
                if(theCookie.name.hasPrefix("x1")) { self.UUU.x1= theCookie.value }
                if(theCookie.name.hasPrefix("x2")) { self.UUU.x2= theCookie.value }


            print("SUCCESS")
            self.tokenReloader()
        })
        task.resume()

    }

func tokenReloader() {


            let context: JSContext? = {
                let context = JSContext()

                // 1
                guard let
                    commonJSPath = Bundle.main.path(forResource: "x3", ofType: "js") else {
                        print("Unable to read resource files.")
                        return nil
                }

                // 2
                do {
                    let common = try String(contentsOfFile: commonJSPath, encoding: String.Encoding.utf8)
                    _ = context?.evaluateScript(common)
                } catch (let error) {
                    print("Error while processing script file: \(error)")
                }

                return context
            }()

            let parseFunction = context!.objectForKeyedSubscript("x3")
            let parsed = parseFunction?.call(withArguments: [UUU.encryptedToken!]).toString()


            requestT(withTokensOf: UUU)

    //}

     //   else { attempts = attempts + 1 }

    }

func requestT(withTokensOf theUUU : UUUClass) {

        let _UUU = theUUU
        for (index, theWatching) in watchingList.enumerated() {

            _UUU.dictToProps(dict: theWatching.propsToDict())

            self.makeRequest("**some_post_request**", url: "...", num: index, _UUU: _UUU)

        }

    }

    func makeRequest(_ parameters: String, url:String, num: Int, _UUU: UUUClass){
    let uuu = _UUU
    let postData:Data = parameters.data(using: String.Encoding.utf8)!
    //let postLength:NSString = String(postData.length)
    let request = NSMutableURLRequest(url: URL(string: url)!)
    let session = URLSession.shared
    request.httpMethod = "POST"
    request.httpBody = postData

    //request.setValue("text/html,application/xhtml+xml,application/xml;q=0.9,*;q=0.8", forHTTPHeaderField: "Accept")
    request.setValue("*/*", forHTTPHeaderField: "Accept")

    //print(request.allHTTPHeaderFields)

    let task = session.dataTask(with: request as URLRequest, completionHandler: {
        (data1, response, error) -> Void in
        guard error == nil && data1 != nil else {
            let _error = error as? NSError
            let themessage : String

            print("error=\(_error?.code)")
            return
        }

        let httpResponse = response as! HTTPURLResponse
        let statusCode = httpResponse.statusCode
        print(statusCode)
        if (statusCode == 200) {
            print("Everyone is fine, data received successfully.")
            print("response: \(response); data: \(data1)")

            let json = JSON(data: data1!)
            if (json["error"].boolValue == true) { return }
            print("JSON received: \(json.description)")

        }
        else {
            print("Error - no response")
             }
    })

    task.resume()
}

【问题讨论】:

  • 你能展示你试过的代码吗?您应该通过调用backgroundTaskWithExpirationHandler 来启动后台任务,然后执行您的网络操作。操作完成后,结束任务。您在后台只有 3 分钟的总执行时间,因此您最终可能会耗尽它。如果用户将您的应用返回到前台,则时间会重置。
  • 提供您将很快获得反对票的代码
  • @Paulw11 添加了我的代码。由于专有规则,一些细节被删除,但逻辑被保存
  • @Paulw11 backgroundTaskWithExpirationHandler 帮助了,谢谢。还有一个问题:每个新的 backgroundTask 或总和失效后,backgroundTimeRemaining 是否会重置?为什么执行任务前的时间比执行后的时间短?

标签: ios swift background push nsurlsession


【解决方案1】:

您的问题几乎可以肯定是不可能的,而且有充分的理由。一个应用程序每两分钟下载一个小资源,基本上就是让蜂窝无线电几乎一直处于开启状态。即使在最好的情况下,这也是一个巨大的电池消耗——几乎和连续打电话一样糟糕。

理论上,您可以使用 backgroundTaskWithExpirationHandler 让您在两分钟后做某事,并在您之前的请求完成或失败两分钟后开始新的请求。

实践中,如果您的应用程序经常在后台唤醒并开始新的请求,则 NSURLSession 机制会故意在唤醒您的应用程序时引入延迟。一段时间后,我相信你的运行频率将远低于每两分钟运行一次,虽然我没有查看 NSURLSession 的那部分代码,所以我不能确定你是否会超过它的阈值。

无论如何,这是一个非常糟糕的主意。在后台获取偶尔的数据是后台获取的目的,如果您发出的请求比后台获取的频率更高,这意味着您正在做一些您不应该做的事情,这是一个好方法让您的应用被拒绝。

如果您真的需要以近乎实时的方式了解应用程序外部的信息,这就是推送通知的用途。

【讨论】:

  • 谢谢,我已经用 backgroundTaskWithExpirationHandler 解决了这个问题。我确实需要每隔几分钟就检查一次信息。实际上,用户了解并了解电池消耗。当他们不需要这个应用时,他们可以直接杀掉它。
  • 您可能会发现它在四到五个周期后停止工作,并且开始以指数方式更慢地更新,但可能不会。祝你好运。
猜你喜欢
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 2019-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 2013-02-06
相关资源
最近更新 更多