通知操作后台任务未按预期运行
Posted
技术标签:
【中文标题】通知操作后台任务未按预期运行【英文标题】:Notification action background task not running as expected 【发布时间】:2017-07-26 15:15:58 【问题描述】:更新
在一些 cmets 之后,我将 resolveVisita
方法更改为在后台使用 URLSession
,但看起来后台会话仅在应用程序处于前台时才会启动,这是在通知时运行的更新方法动作被点击:
static func resolverVisita(idMensagem: String, resposta: String, liberar: Bool, view: UIViewController?)
//let configuration = URLSessionConfiguration.background(withIdentifier: "url_session_resolve_visita")
//var backgroundSession = URLSession(configuration: configuration)
// Set up the URL request
let todoEndpoint: String = URL_RESPONDE_VISITA
guard let url = URL(string: todoEndpoint) else
print("Error: cannot create URL")
return
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
let postString = "userUUID=\(SessionManager.getUsrUUID())&devUUID=\(devUUID)&msgID=\(idMensagem)&tarefa=\(liberar ? "L" : "B")&resposta=\(resposta)"
urlRequest.httpBody = postString.data(using: .utf8)
let config = URLSessionConfiguration.background(withIdentifier: "\(Bundle.main.bundleIdentifier!).responde_visita")
let session = URLSession(configuration: config)
/*
let task = URLSession.shared.dataTask(with: urlRequest) data, response, error in
guard let data = data, error == 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 = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
*/
let task = session.dataTask(with: urlRequest)
task.resume()
注释掉的代码有时似乎可以工作,但看起来它不是正确的做法。
更新结束
所以我有一个接收通知的应用程序,它有 2 个操作,根据用户响应,应用程序应该向服务器发送响应,最好是在后台。
我面临一个问题,即每当点击某个操作时,有时在打开应用程序之前不会调用 userNotificationCenter
方法,有时它会运行,但 Alamofire 服务器调用不会得到处理,然后如果我打开应用程序,控制台中会显示有关 Alamofire 调用的一些错误,但是如果我点击该操作然后足够快地打开应用程序,它会按预期工作。
我已经在 Xcode 的应用功能下启用了后台获取和远程通知。
这就是我为通知创建操作的方式:
let liberar = UNTextInputNotificationAction(identifier:"liberar", title:"Liberar",options:[.authenticationRequired],
textInputButtonTitle: "Liberar",
textInputPlaceholder: "Resposta")
let bloquear = UNTextInputNotificationAction(identifier: "bloquear", title: "Bloquear", options: [.destructive],
textInputButtonTitle: "Bloquear",
textInputPlaceholder: "Resposta")
这是我的UNUserNotificationCenterDelegate
协议实现:
extension AppDelegate : UNUserNotificationCenterDelegate
// Receive displayed notifications for ios 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
let userInfo = notification.request.content.userInfo
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.messaging().appDidReceiveMessage(userInfo)
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey]
print("Message ID: \(messageID)")
// Print full message.
print(userInfo)
// Change this to your preferred presentation option
completionHandler([.alert, .badge, .sound])
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void)
let userInfo = response.notification.request.content.userInfo
let acao = response.actionIdentifier
//let request = response.notification.request
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey]
print("Message ID: \(messageID)")
print(acao)
if acao == "liberar"
// Print full message.
let textResponse = response as! UNTextInputNotificationResponse
print("Liberando Visita")
AppConfig.resolverVisita(idMensagem: (userInfo["msgid"] as? String)!, resposta: textResponse.userText, liberar: true, view: nil)
else if acao == "bloquear"
let textResponse = response as! UNTextInputNotificationResponse
print("Bloqueando Visita")
AppConfig.resolverVisita(idMensagem: (userInfo["msgid"] as? String)!, resposta: textResponse.userText, liberar: false, view: nil)
//let newContent = request.content.mutableCopy() as! UNMutableNotificationContent
//print(textResponse.userText)
completionHandler()
这里是 resolveVisita
方法,它基本上只是收集一些数据并使用 Alamofire 通过 POST 请求发送。
static func resolverVisita(idMensagem: String, resposta: String, liberar: Bool, view: UIViewController?)
if view != nil
showLoading(mensagem: NSLocalizedString("Processando...", comment: ""), view: view!)
//Parameters to be sent
let parameters: Parameters=[
"userUUID":SessionManager.getUsrUUID(),
"devUUID":devUUID,
"msgID": idMensagem,
"resposta": resposta,
"tarefa": liberar ? "L" : "B"
];
//Sending http post request
Alamofire.request(URL_RESPONDE_VISITA, method: .post, parameters: parameters).responseJSON
response in
//printing response
print(response)
//getting the json value from the server
if let result = response.result.value
//converting it as NSDictionary
let jsonData = result as! NSDictionary
var mensagem : String = "Solicitacao resolvida com sucesso"
//displaying the message in label
if((jsonData["error"] as! Bool))
mensagem = jsonData["error_msg"] as! String
if view == nil
Notificacoes.notificaErroSolicitavao(msgId: idMensagem, errMsg: mensagem)
if view != nil
view?.dismiss(animated: false)
showAlert(mensagem: NSLocalizedString(mensagem, comment: ""), view: view!, okMsg: NSLocalizedString("Voltar", comment: ""), segue: "voltarParaLogin")
else
print(mensagem)
如果我只是将.foreground
选项添加到使应用程序在被选中后打开的操作中,问题就解决了,但是我真的认为没有必要为此任务打开应用程序。
【问题讨论】:
我建议启动后台URLSession
请求(这对于 Alamofire 来说确实很笨拙),或者至少在异步 responseJSON
完成处理程序之后才调用通知完成处理程序叫做。后一种方法的典型解决方案是为您的方法本身提供一个完成处理程序,您将在 responseJSON
闭包结束时调用。然后将对用户通知完成处理程序的调用移动到该闭包中。
@Rob 哦,我现在明白了,当调用完成处理程序时,后台任务被杀死,所以我必须找到一种方法来调用 alamofire 响应的完成句柄。对吗?
是的,就是这样。而且您通常只有几秒钟(30?),这就是为什么如果不太可能很快收到回复,您会使用后台URLSession
。
@Rob 我实际上不需要响应是公平的,因为服务器会为响应生成另一个通知,所以我现在会尝试,如果我遇到问题我可能会检查URLSession
嘿,@Rob 先生,所以我更改了我的 resolveVisita
方法以使用 URLSession 背景而不是 alamofire,我创建了会话,请求执行了 task.resume,但它没有从通知中运行!它仅在应用程序打开时才有效。我是否必须设置一些特殊权限才能从通知或锁定屏幕启动后台会话?
【参考方案1】:
所以一段时间后,我决定以全新的心态回到这个问题,我发现我做错了什么,我试图在锁定屏幕的背景中创建 URLSession
的实例,并创建一个“相同会话”的新实例,为了解决这个问题,我所要做的就是创建一个 URLSession
的静态实例,然后在需要时使用它。
我是这样创建的。
static let resolveVisitaSession = URLSession(configuration: URLSessionConfiguration.background(withIdentifier: "br.com.freaccess.resolveVisitaRequest"))
这是在选择 UNNotificationAction
时调用的方法,我在那里使用我的 URLSession。
static func resolverVisita(idMensagem: String, resposta: String, liberar: Bool)
print("Entered resolveVisita method")
// Set up the URL request
let todoEndpoint: String = URL_RESPONDE_VISITA
guard let url = URL(string: todoEndpoint) else
print("Error: cannot create URL")
return
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
let postString = "userUUID=\(SessionManager.getUsrUUID())&devUUID=\(devUUID)&msgID=\(idMensagem)&tarefa=\(liberar ? "L" : "B")&resposta=\(resposta)&skipSocket=true"
urlRequest.httpBody = postString.data(using: .utf8)
resolveVisitaSession.dataTask(with: urlRequest).resume()
【讨论】:
以上是关于通知操作后台任务未按预期运行的主要内容,如果未能解决你的问题,请参考以下文章