iOS 操作扩展中的后台任务
Posted
技术标签:
【中文标题】iOS 操作扩展中的后台任务【英文标题】:Background task in iOS action extension 【发布时间】:2018-11-14 12:40:23 【问题描述】:我正在为一个应用程序开发一个操作扩展程序,它为用户准备一些数据,然后使用 MailCore2 将此信息发送到 SMTP 服务器。准备数据的速度非常快,但发送电子邮件可能需要一些时间(取决于其大小)。这就是为什么我正在寻找一种在后台处理发送活动的方法。但这会导致不同解决方案出现一些问题:
使用 URLSession。这是在 ios 扩展中处理大量上传或下载的第一种方法。但问题是 MailCore2 不使用 URLSessions 将数据传输到邮件服务器。所以这是不可用的。或者是否有可能以某种方式将此调用“包装”在 URLSession 中?
使用 UNUserNotificationCenter 并在数据准备好后从扩展发送本地通知。想法是在收到此通知后在主应用程序中启动发送任务。问题:仅在用户单击通知时调用通知userNotificationCenter didReceive
方法。简单的徽章通知不会调用委托方法。
使用后台获取。这在模拟器中运行良好,但在 iOS 设备上连接到 SMTP 服务器时出现错误。我遇到了与github.com/MailCore/mailcore2/issues/252 中描述的相同的 HELO 问题。对于这个问题,解决方案应该是MCOSMTPSession.isUseHeloIPEnabled = true
,但这可能不适用于所有服务器。所以也不是一个完美的解决方案。
任何想法,如何处理这样的任务?或者如何处理上面提到的解决方案之一?
编辑:因为这个问题还没有得到任何答案:什么不清楚或需要哪些额外信息?
【问题讨论】:
嗨,我想你在这里所做的可以回答我的问题。 Can you please share your experience? 为什么不使用 MCOAttachment 将附件与您的电子邮件一起发送?并将您的应用设置为在后台运行。 如何设置应用程序在后台运行?我已经使用 MCOAttachment。我尝试使用问题中提到的三种解决方案之一。所有这些都是我实现后台活动的尝试。但它们都没有产生有效的结果。 问题,当扩展程序运行时,您的原始应用程序是否在后台? 不,应用程序没有在后台运行(或者至少不能保证)。该扩展可以访问处理其任务所需的所有代码/库。问题是:如何在后台运行这段代码(=发送邮件),让用户无需等待完成。 【参考方案1】:您似乎无法通过扩展在后台安排长时间运行的任务。请参阅here(Some APIs Are Unavailable to App Extensions)
在您建议的解决方案中,我的建议是尝试使用 silent push notifications ,您可以在准备数据阶段调用 api,然后从服务器发送静默推送并执行后台任务当无声推送到来时。
【讨论】:
我心系无声通知。但作为 Vyacheslav 的回答,它需要额外的服务器来处理推送通知。有什么想法,如果本地通知(如闹钟)也可以静音?【参考方案2】:从您的回复来看,您的困难在于您需要允许后台活动。您可以通过following a tutorial such as this one 进行操作。
【讨论】:
感谢您的回答!我的应用程序已启用后台活动。我检查了教程。描述了不同的背景模式。其中两个适合我想在后台执行的内容/任务:Executing Finite-Length Tasks - 这个不能从扩展中使用,因为它需要访问UIApplication.shared
。另一个是 Background fetch - 这导致了我在问题中描述的问题。
您找到解决此问题的方法了吗?
@cdn34 检查我上面的答案:***.com/a/60945640/768352【参考方案3】:
最有用的解决方案是使用推送通知。 我在自己的应用程序中使用的解决方案:
-
创建一个可以检索简单请求的简单服务器:设备令牌和您想要唤醒设备的时间。
当应用程序进入后台模式时,向服务器发送请求以稍后唤醒设备。例如。 5分钟/20分钟等
func applicationWillEnterForeground(_ application: UIApplication)
内AppDelegate
。
不要忘记让应用程序尽可能在后台运行。
例如,
@UIApplicationMain
class AppDelegate: UIResponder
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
if UIApplication.shared.applicationState != .active
doFetch(completionHandler: completionHandler)
else
// foreground here
completionHandler(UIBackgroundFetchResult.noData)
func application(_ application: UIApplication,
performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
doFetch(completionHandler: completionHandler)
private func doFetch(completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
sendTheRequestToWakeApp()
backgroundTask = UIApplication.shared.beginBackgroundTask [weak self] in
self?.endBackgroundTask()
/// do work here
completionHandler(UIBackgroundFetchResult.noData)
private func endBackgroundTask()
print("Background task ended.")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
private func sendTheRequestToWakeApp()
/// Implement request using native library or Alamofire. etc.
在服务器端使用简单的时间或循环。
缺点,
-
需要互联网
它并不完美。当电池电量不足时,后台模式会受到限制。
别忘了设置项目:
【讨论】:
这是个好主意。唯一需要发送推送通知的附加服务器。目前它是一个独立的应用程序,没有额外的要求。不过,可以使用 NSUrlSession 将信息发送到服务器。你知道没有互联网连接时 NSUrlSession 的行为吗?它会重试还是直接退出请求?【参考方案4】:经过多次测试和失败,我找到了以下解决方案,可以在扩展的后台执行长时间执行的任务。即使扩展已经完成,这也可以按预期工作:
func performTask()
// Perform the task in background.
let processinfo = ProcessInfo()
processinfo.performExpiringActivity(withReason: "Long task") (expired) in
if (!expired)
// Run task synchronously.
self.performLongTask()
else
// Cancel task.
self.cancelLongTask()
此代码使用ProcessInfo.performExpiringActivity()
在另一个线程中执行任务。 performLongTask()
中的任务是同步执行的,这一点很重要。当到达块的末尾时,线程将终止并结束您的任务的执行。
类似的方法也适用于主应用程序。在background tasks in iOS的小总结中有详细描述。
【讨论】:
以上是关于iOS 操作扩展中的后台任务的主要内容,如果未能解决你的问题,请参考以下文章
iOS开发简单的实现后台任务(诸如后台播放音乐,定时器,后台定位等)