大家都知道,当用户点击iPhone的Home键时,iOS会把当前的app转移到background状态,在background状态停留短暂的时间再转移到suspended,这样做iOS能节省系统资源,提高电池的使用周期。但是如果我们需要在后台执行一些任务呢?需要保持app在background状态?别担心,iOS已经为我们提供了三种策略:
1、当app需要在后台执行有限长度的任务时
2、当app需要支持在后台下载文件时
3、app需要在background执行长时间特定类型的人去
app需要在后台执行有限长度的任务
当app从background进入到suspended,需要执行耗时不长的任务,比如保存数据之类的,可以调用UIApplication对象的beginBackgroundTaskWithName:expirationHandler:或者beginBackgroundTaskWithExpirationHandler:方法,请求额外的时间执行任务,下面看demo:
//this is Swift
func applicationDidEnterBackground(_ application: UIApplication) {
bgTask = application.beginBackgroundTask(withName: "myTask") {
print("超过执行最长时间...")
application.endBackgroundTask(self.bgTask!)
self.bgTask = UIBackgroundTaskInvalid
}
DispatchQueue.global().async {
print("后台执行任务.....")
DispatchQueue.global().asyncAfter(deadline: .now() + 12) {
print("任务完成")
application.endBackgroundTask(self.bgTask!)
self.bgTask = UIBackgroundTaskInvalid
}
}
}
//执行结果
后台执行任务.....
任务完成
每一次调用beginBackgroundTaskWithName:expirationHandler: 或 beginBackgroundTaskWithExpirationHandler: 方法都会生成独特的token关联这个任务,当完成任务,必须使用相应的token调用endBackgroundTask: 方法,让系统知道这个任务执行完成,如果在一定的时间没有调用endBackgroundTask: ,系统将调用expirationHandler给app最后一次机会结束任务
在Background状态下载内容
要支持在后台下载内容,app必须用NSURLSession对象下载,这样系统才能在app被suspended或terminated时,控制下载进程。app使用NSURLSession并配置了后台下载,当app被终止,系统将接管下载任务,在下载完成时或需要app关注时,系统将在后台唤醒app。
为了支持Background传输,需要对NSURLSession对象进行配置,demo如下:
//配置session支持后台下载
let configuration = URLSessionConfiguration.background(withIdentifier: "background downloading")
configuration.sessionSendsLaunchEvents = true
configuration.isDiscretionary = true
var session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
配置好后,URLSession对象会在合适的时间把上传下载任务转交给系统。
- 如果任务在foreground或者background完成,会正常的调起相应的delegate;
- 如果系统在终止app前,还没有下载完成,系统会自动在后台继续完成任务,当完成后系统会重新打开app(假设不是用户强制退出app),调用app delegate’s 的application:handleEventsForBackgroundURLSession:completionHandler:方法,app需要在方法里实现,用提供的identifier创建新的URLSessionConfiguration 和 NSURLSession对象,系统将新的session对象重新连接到先前的任务,并调用相应的delegate
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
let configuration = URLSessionConfiguration.background(withIdentifier: identifier)
configuration.sessionSendsLaunchEvents = true
configuration.isDiscretionary = true
var session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}
实现长时间执行任务
如果需要更长执行时间,app需要请求特殊的权限在background执行而不进入suspended状态,在iOS,只有几种特殊类型的app才允许运行在后台:
- 在后台播放音视频类的app,比如音乐播放器
- 在后台录音类的app
- 需要实时通知用户坐标的app,比如导航类app
- 支持VoIP的app
- 需要定期下载和处理新内容的应用程序
- 从外部配件接收定期更新的应用程序
这些类型的app必须声明其支持的服务,并使用系统框架实现这些服务
声明app支持的背景任务
在TARGETS选中项目,点击Capabilities页,开启Background Modes,选择相应的model,如下图选择了Audio and AirPlay
因为篇幅有限,这里只给大家举一个例子,有兴趣的同学可自行研究
追踪用户的位置信息
一款跑步软件,在跑步时需要记录用户的位置信息,并实时的更新时长和公里数,就需要app能在后台长时间的运行,demo如下:
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.activityType = .fitness
if #available(iOS 9.0, *) {
//允许app在后台执行位置更新
locationManager.allowsBackgroundLocationUpdates = true
}
locationManager.distanceFilter = 10.0
locationManager.startUpdatingLocation()
locationManager.delegate = self
只有在allowsBackgroundLocationUpdates为true和Capabilities页开启Background Modes,选择了Location updates,才能在后台运行,并在delegate执行相应的逻辑
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations {
let howRecent = location.timestamp.timeIntervalSinceNow
//筛选loaction(10秒前的坐标位置不要)
if abs(howRecent) >= 10 {
continue;
}
//筛选loaction(精度不达标的位置信息不要)
if location.horizontalAccuracy < kCLLocationAccuracyNearestTenMeters * 3 && location.horizontalAccuracy > 0 {
if !self.locations.isEmpty{
//计算跑步的距离
distance += location.distance(from: self.locations.last!)
}
self.locations.append(location)
}
}
}