我正在编写一个需要使用高精度和低频率进行背景位置更新的应用程序。该解决方案似乎是一个后台NSTimer任务,它启动位置管理器的更新,然后立即关闭。之前已经问过这个问题:
How do I get a background location update every n minutes in my iOS application?
Getting user location every n minutes after app goes to background
iOS Not the typical background location tracking timer issue
iOS long-running background timer with "location" background mode
iOS full-time background-service based on location tracking
但我还没有得到最小示例。在尝试了上述接受的答案的每一个排列后,我总结了一个起点。输入背景:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"ending background task");
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
self.timer = [NSTimer scheduledTimerWithTimeInterval:60
target:self.locationManager
selector:@selector(startUpdatingLocation)
userInfo:nil
repeats:YES];
}
和委托方法:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(@"%@", newLocation);
NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self.locationManager stopUpdatingLocation];
}
当前行为是backgroundTimeRemaining
从180秒递减到零(记录位置时),然后执行到期处理程序,不再生成进一步的位置更新。如何修改上述代码,以便无限期地在后台接收定期位置更新?
更新:我的目标是iOS 7,似乎有一些证据表明后台任务的行为有所不同:
答案 0 :(得分:61)
似乎stopUpdatingLocation
触发了后台监视计时器,因此我在didUpdateLocation
中将其替换为:
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
[self.locationManager setDistanceFilter:99999];
似乎可以有效地关闭GPS。然后,背景NSTimer
的选择器变为:
- (void) changeAccuracy {
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}
我正在做的就是定期切换精度以便每隔几分钟获得一次高精度坐标,并且因为locationManager
尚未停止,backgroundTimeRemaining
保持其最大值。这样可以减少电池消耗,从每小时约10%(背景中恒定为kCLLocationAccuracyBest
)到我的设备每小时约2%。
答案 1 :(得分:45)
我确实使用位置服务编写了应用,应用必须每10秒发送一次位置。 而且效果很好。
只需使用" allowDeferredLocationUpdatesUntilTraveled:timeout"方法,遵循Apple的文档。
步骤如下:
必填:注册后台模式以获取更新位置。
1。创建LocationManger和startUpdatingLocation,精确度和filteredDistance为您想要的任何内容:
-(void) initLocationManager
{
// Create the manager object
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
_locationManager.delegate = self;
// This is the most important property to set for the manager. It ultimately determines how the manager will
// attempt to acquire location and thus, the amount of power that will be consumed.
_locationManager.desiredAccuracy = 45;
_locationManager.distanceFilter = 100;
// Once configured, the location manager must be "started".
[_locationManager startUpdatingLocation];
}
2. 使用" allowDeferredLocationUpdatesUntilTraveled:timeout"使应用程序永远保持运行状态在后台方法中,当app移动到后台时,必须使用新参数重新启动updatesLocation,如下所示:
- (void)applicationWillResignActive:(UIApplication *)application {
_isBackgroundMode = YES;
[_locationManager stopUpdatingLocation];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[_locationManager setDistanceFilter:kCLDistanceFilterNone];
_locationManager.pausesLocationUpdatesAutomatically = NO;
_locationManager.activityType = CLActivityTypeAutomotiveNavigation;
[_locationManager startUpdatingLocation];
}
3. 应用程序正常使用" locationManager获取updatedLocations:didUpdateLocations:"回调:
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// store data
CLLocation *newLocation = [locations lastObject];
self.userLocation = newLocation;
//tell the centralManager that you want to deferred this updatedLocation
if (_isBackgroundMode && !_deferringUpdates)
{
_deferringUpdates = YES;
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
}
}
4. 但是你应该处理数据" locationManager:didFinishDeferredUpdatesWithError:"为您的目的回电
- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {
_deferringUpdates = NO;
//do something
}
5. 注意: 我认为我们应该在每次应用在背景/地面模式之间切换时重置LocationManager的参数。
答案 2 :(得分:34)
如果您的plist中UIBackgroundModes
带有location
密钥,则无需使用beginBackgroundTaskWithExpirationHandler
方法。那是多余的。你也错误地使用它(见here),但是因为你的plist被设置了,所以没有实际意义。
在plist中使用UIBackgroundModes location
,只要CLLocationManger
正在运行,应用就会无限期地继续在后台运行。如果您在后台呼叫stopUpdatingLocation
,则该应用将停止并且不会再次启动。
也许您可以在致电beginBackgroundTaskWithExpirationHandler
之前致电stopUpdatingLocation
,然后在致电startUpdatingLocation
之后,您可以拨打endBackgroundTask
以便在GPS停止时保持后挡,但我从来没有尝试过 - 这只是一个想法。
另一个选项(我还没试过)是让位置管理器在后台运行但是一旦你得到一个准确的位置,将desiredAccuracy
属性更改为1000米或更高,以允许GPS芯片获得关闭(节省电池)。然后10分钟后,当您需要更新其他位置时,请将desiredAccuracy
更改回100米以打开GPS,直到获得准确的位置,然后重复。
当您在位置经理上致电startUpdatingLocation
时,您必须给它时间来获得一个位置。您不应立即致电stopUpdatingLocation
。我们让它最多运行10秒钟,或直到我们获得非缓存的高精度位置。
您需要过滤掉缓存的位置并检查所获得位置的准确性,以确保其符合最低要求的准确度(请参阅here)。您获得的第一个更新可能是10分钟或10天。您获得的第一个准确度可能是3000米。
请考虑使用重要的位置更改API。获得重要更改通知后,您可以启动CLLocationManager
几秒钟以获得高精度位置。我不确定,我从未使用重要的位置变更服务。
答案 3 :(得分:8)
当需要启动位置服务并停止后台任务时,应该延迟后台任务(1秒应该足够)。否则位置服务不会启动。位置服务也应保持开启几秒钟(例如3秒)。
cocoapod APScheduledLocationManager允许每隔 n 秒获取背景位置更新,并具有所需的位置准确性。
let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)
存储库还包含一个用Swift 3编写的示例应用程序。
答案 4 :(得分:1)
如何试用startMonitoringSignificantLocationChanges:
API?它肯定会以较低的频率发射,并且准确性相当好。此外,它比使用其他locationManager
API更具优势。
有关此API的更多信息已在此link
上进行了讨论答案 5 :(得分:1)
您需要在info.plist中添加以下密钥,在您的应用程序中添加位置更新模式。
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
将调用 didUpdateToLocation
方法(即使您的应用处于后台)。您可以在此方法调用的基础上执行任何操作
答案 6 :(得分:0)
在iOS 8之后,他们对Apple提供的CoreLocation框架相关的后台提取和更新进行了一些更改。
1)Apple引入了AlwaysAuthorization请求,用于在应用程序中获取位置更新。
2)Apple引入了backgroundLocationupdates,用于从iOS中的后台获取位置。
要在后台获取位置,您需要在Xcode的功能中启用位置更新。
//
// ViewController.swift
// DemoStackLocation
//
// Created by iOS Test User on 02/01/18.
// Copyright © 2018 iOS Test User. All rights reserved.
//
import UIKit
import CoreLocation
class ViewController: UIViewController {
var locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
startLocationManager()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
internal func startLocationManager(){
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
}
}
extension ViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let location = locations[0] as CLLocation
print(location.coordinate.latitude) // prints user's latitude
print(location.coordinate.longitude) //will print user's longitude
print(location.speed) //will print user's speed
}
}
答案 7 :(得分:0)
要在应用程序处于后台运行时使用标准位置服务,您需要首先在“目标”设置的“功能”标签中打开“后台模式”,然后选择“位置更新”。
或直接将其添加到 Info.plist 。
<key>NSLocationAlwaysUsageDescription</key>
<string>I want to get your location Information in background</string>
<key>UIBackgroundModes</key>
<array><string>location</string> </array>
然后您需要设置CLLocationManager
目标C
//The Location Manager must have a strong reference to it.
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
//Request Always authorization (iOS8+)
if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { [_locationManager requestAlwaysAuthorization];
}
//Allow location updates in the background (iOS9+)
if ([_locationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) { _locationManager.allowsBackgroundLocationUpdates = YES;
}
[_locationManager startUpdatingLocation];
快速
self.locationManager.delegate = self
if #available (iOS 8.0,*) {
self.locationManager.requestAlwaysAuthorization()
}
if #available (iOS 9.0,*) {
self.locationManager.allowsBackgroundLocationUpdates = true
}
self.locationManager.startUpdatingLocation()
参考:https://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba
答案 8 :(得分:-1)
locationManager.allowsBackgroundLocationUpdates = true