Background Modes
内容来源自:Background Modes Tutorial: Getting Started
1.播放音频
在后台播放音频启用如下的设置:
2.定位更新
即使App在后台,也能收到定位更新的消息
如下的方式,创建CLLocationManager
:
private lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
//1. request authorization
manager.requestAlwaysAuthorization()
if #available(iOS 9, *) {
manager.allowsBackgroundLocationUpdates = true
}
return manager
}()
注意requestAlwaysAuthorization
只在iOS8及以后版本有效
设置background modes
另外还要在Info.plist中添加key,解释为什么要使用定位:
- Privacy — Location Always and When In Use Usage Description
- Privacy — Location When In Use Usage Description
如果是在模拟器上跑的话,使用如下的设置来来模拟位置更新:
3.Finite-Length Tasks
从技术上来讲,Finite-Length Task并不是background mode,所以并不需要在Capabilities中进行设置。实际上,它是,当App在后台时,允许你在一段有限的时间内执行任何代码
对Finite-Length Tasks的理解,可参考:Executing a Finite-Length Task in the Background
如果你的app正在执行task中,并需要完成这个task,可通过调用
UIApplication
对象的beginBackgroundTaskWithName:expirationHandler: 或者 beginBackgroundTaskWithExpirationHandler:方法,来请求额外的执行时间。调用这些方法会暂时延迟app的暂停,给你一些时间来完成task。在完成后,需调用endBackgroundTask:方法来让系统知道task已完成,可以被suspended
每次调用beginBackgroundTaskWithName:expirationHandler:
或者beginBackgroundTaskWithExpirationHandler:
方法,都会生成一个唯一的token,与相应的task绑定在一起。当app完成了task,需调用endBackgroundTask:
方法,带上生成的token,让系统知道task已完成。
如果在启动task的时候,你提供了一个 expiration handler,系统会调用这个handler,此时你有最后的一次机会来结束task
并不需要在app进入后台后才指定后台task。通常的做法是,在启动一个task之前,就调用beginBackgroundTaskWithName:expirationHandler:
或者beginBackgroundTaskWithExpirationHandler:
方法,在结束后调用endBackgroundTask:
方法
由iOS来决定app进入后台后你还有多少时间,并不能保证一定有多少时间,可通过检查UIApplication.shared
的backgroundTimeRemaining
属性,来获取所剩的时间
The general, observation-based consensus is that you get three minutes
通常来说有3分钟
这个例子是计算斐波那契数列,并且可以在后台计算
func registerBackgroundTask() {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
}
assert(backgroundTask != .invalid)
}
func endBackgroundTask() {
print("Background task ended.")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
4.Background Fetch
Background Fetch在iOS7中被引入。例如,要在app中实现一个动态消息的功能,在使用Background Fetch之前,你可能会在viewWillAppear(_:)
方法中来获取新的数据。
这样做所带来的问题是,用户在最初的几秒看到的是旧数据,直到获取到新的数据。如果用户一打开app,就能展示新的数据,这样做岂不是更好?
当启用Background Fetch后,系统会决定启用Background Fetch的时机。例如,如果你的用户通常在9AM打开app,Background Fetch的时机非常有可能在这个时间之前。
设置Background Fetch
-
Capabilities中的Background Modes勾上Background fetch
-
使用
setMinimumBackgroundFetchInterval(_:)
方法设置时间间隔 -
在app delegate中实现
application(_:performFetchWithCompletionHandler:)
方法,来处理background fetch
就像上面的名称所暗示的那样,background fetch通常从外部获取信息,像一个网络请求。
相比较于finite-length tasks
,你大概只有几秒钟来做background fetch
——共识是大概最多30秒。如果你需要下载大的资源,最好使用URLSession
的background transfer service
在AppDelegate
中进行如下的设置:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UIApplication.shared.setMinimumBackgroundFetchInterval(UIApplication.backgroundFetchIntervalMinimum)
return true
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if let fetchViewController = window?.rootViewController as? FetchViewController
{
fetchViewController.fetch {
fetchViewController.updateUI()
completionHandler(.newData)
}
}
}
调试:
其它文档: