后台任务未在 Swift 中调用类

Posted

技术标签:

【中文标题】后台任务未在 Swift 中调用类【英文标题】:Background task not calling class in Swift 【发布时间】:2018-07-12 09:57:44 【问题描述】:

我有一个后台任务,它应该返回一系列课程,然后发送到 Apple Watch。我正在通过WatchConnectivity 调用didReceiveMessage 内部的任务。

后台任务需要执行一些操作,例如打开领域数据库、查询结果和访问文档目录,然后才能将响应返回到课程字典。当手表输出获取课程数据时,逻辑似乎起作用。问题是我不认为后台任务实际上是在调用方法getWatchCourses()

DispatchQueue.global().async 

    var foundCourses = [[String : Any]]()
    let application = UIApplication.shared
    backgroundTask = application.beginBackgroundTask(withName: "app.test.getCourses") 
        let watchModel = WatchCourseModel()
        let courses = watchModel.getWatchCourses()
        print("Courses: \(courses)")
        foundCourses.append(contentsOf: courses)
    
    replyHandler(["response": foundCourses])
    application.endBackgroundTask(backgroundTask)
    backgroundTask = UIBackgroundTaskInvalid

如果getWatchCourses() 结果是硬编码的,这也不起作用。应用程序可以在后台执行此逻辑吗?还是应该正常工作?

还值得指出的是,网上没有任何地方对此进行记录, 他们总是指将简单的文本响应发送回手表, 不是处理器密集型的:(

谢谢

【问题讨论】:

【参考方案1】:

您在用于清理的尾随闭包中进行操作。还有更多的事情是你一开始就结束后台任务,所以你的闭包永远不会调用。

这是后台任务如何工作的工作示例

 var backgroundTaskIdentifier:UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
 var updateTimer: Timer?

现在,当您开始运行计时器示例时

  updateTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self,
                                     selector: #selector(calculateNextNumber), userInfo: nil, repeats: true)
  // register background task
  beginBackgroundTask()

当你结束计时器时,结束后台任务;应该总是结束后台任务而不会失败

func beginBackgroundTask () 
    backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler:  [weak self] in
      self?.endBackgroundTask() // It will call to cleanup 
    )
    assert(backgroundTaskIdentifier != UIBackgroundTaskInvalid)
  

  func endBackgroundTask () 
    UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
    backgroundTaskIdentifier = UIBackgroundTaskInvalid
  

所以在你的情况下

注册后台任务 查询您的数据库(执行您的所有操作) 当您完成通话结束后台任务时

注意:还要注意苹果给出的执行后台任务的时间限制

编辑

确保您已启用后台功能表单项目设置

试试看

    beginBackgroundTask()

    let watchModel = WatchCourseModel()
    let courses = watchModel.getWatchCourses()
    print("Courses: \(courses)")
    foundCourses.append(contentsOf: courses)
    endBackgroundTask()

【讨论】:

所以我将业务逻辑放在 beginBackgroundTask 中? 您应该连同您的业务逻辑一起启动后台任务。当你完成它时,你应该调用 end background task 困惑,let courses = watchModel.getWatchCourses() 等会去哪里?在beginBackgroundTask() 内部还是其他地方? 复制粘贴后台任务开始和结束的方法。然后检查编辑 后台功能已启用,是否需要选择特定选项?【参考方案2】:

如果你检查documentation,你会看到完整的函数原型是beginBackgroundTask(withName:expirationHandler:)

您通过尾随闭包指定的代码是expirationHandler - 如果您在允许的后台执行时间用完之前不调用endBackgroundTask,则会调用该代码。过期处理程序使您有最后机会在您的应用因超过其后台时间而终止之前执行任何清理工作。

由于您几乎立即调用endBackgroundTask 并从函数返回,因此不会调用过期处理程序。

你真正想要的是这样的:

var foundCourses = [[String : Any]]()
let application = UIApplication.shared
backgroundTask = application.beginBackgroundTask(withName: "app.test.getCourses") 
DispatchQueue.global().async 
    let watchModel = WatchCourseModel()
    let courses = watchModel.getWatchCourses()
    print("Courses: \(courses)")
    foundCourses.append(contentsOf: courses)
    replyHandler(["response": foundCourses])
    application.endBackgroundTask(backgroundTask)
    backgroundTask = UIBackgroundTaskInvalid

这会启动一个后台任务,然后您异步执行工作并将结果传回手表并结束后台任务。

【讨论】:

此解决方案只是挂起,手机没有响应。 你需要在你的应用程序中设置一个断点,看看它在做什么;你还没有显示getWatchCourses,所以我不能说它是否在做正确的事情。 我在应用程序上运行了getWatchCourses 来测试它的结果,它输出到正确的响应中。我觉得项目设置中可能缺少配置或其他内容,因为它应该可以工作。 从后台 ios 设备测试手表应用响应时,断点也不起作用 - 我不认为。 是的,您只需要告诉 Xcode 调试器按名称附加到您的应用进程即可。【参考方案3】:

您可以使用QOS类型作为.background来创建后台任务

DispatchQueue.global(qos: .background).async 
        //background code
        var foundCourses = [[String : Any]]()
        let application = UIApplication.shared
        backgroundTask = application.beginBackgroundTask(withName: "app.test.getCourses") 
            let watchModel = WatchCourseModel()
            let courses = watchModel.getWatchCourses()
            print("Courses: \(courses)")
            foundCourses.append(contentsOf: courses)
        
        replyHandler(["response": foundCourses])
        DispatchQueue.main.async 
            //your main thread
        
    

【讨论】:

这不会解决问题,因为它仍然在过期处理程序中执行所需的代码。此外,.background 不是推荐的 QoS;该优先级可能会被饿死;最好使用.utility 作为最低 QoS。

以上是关于后台任务未在 Swift 中调用类的主要内容,如果未能解决你的问题,请参考以下文章

(iOS)后台运行下载任务

如何使用 Swift 3 使用后台任务?

[译]图解Android应用的后台任务和提醒

applicationWillTerminate 上的 Swift 后台任务

iOS 后台任务 Swift

现在通过前台一个按钮用ajax 调用后台的java timer 类 执行一个 定时任务,每10秒执