我正在寻找一种方法来获得背景位置更新每n分钟在我的iOS应用程序。我使用的是iOS 4.3,解决方案应该适用于未越狱的iphone。

我尝试/考虑了以下选项:

CLLocationManager startUpdatingLocation/startMonitoringSignificantLocationChanges: This works in the background as expected, based on the configured properties, but it seems not possible to force it to update the location every n minutes NSTimer: Does work when the app is running in the foreground but doesn't seem to be designed for background tasks Local notifications: Local notifications can be scheduled every n minutes, but it's not possible to execute some code to get the current location (without the user having to launch the app via the notification). This approach also doesn't seem to be a clean approach as this is not what notifications should be used for. UIApplication:beginBackgroundTaskWithExpirationHandler: As far as I understand, this should be used to finish some work in the background (also limited in time) when an app is moved to the background rather than implementing "long-running" background processes.

如何实现这些定期的后台位置更新?


当前回答

似乎stopUpdatingLocation是什么触发后台看门狗定时器,所以我在didUpdateLocation替换它:

     [self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
     [self.locationManager setDistanceFilter:99999];

这似乎有效地关闭了GPS。然后,背景NSTimer的选择器变成:

- (void) changeAccuracy {
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}

我所做的是每隔几分钟定期切换精度以获得高精度坐标,因为locationManager没有停止,backgroundtimerremain保持在其最大值。这将我的设备上的电池消耗从每小时~10%(在后台使用恒定的kCLLocationAccuracyBest)降低到每小时~2%

其他回答

附件是基于以下的Swift解决方案:

在info.plist中定义位置更新的App寄存器

保持locationManager一直运行

在BestForNavigation和threekm之间切换kCLLocationAccuracy(5秒获得位置),以避免电池耗尽

这个例子在前台每1分钟更新一次位置,在后台每15分钟更新一次位置。

这个例子适用于运行在iOS 7设备上的Xcode 6 Beta 6。

在App委托中(mapView是一个指向mapView控制器的可选选项)

func applicationDidBecomeActive(application: UIApplication!) {
    if appLaunched! == false { // Reference to mapView used to limit one location update per timer cycle
        appLaunched = true
        var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        var window = appDelegate.window
        var tabBar = window?.rootViewController as UITabBarController
        var navCon = tabBar.viewControllers[0] as UINavigationController
        mapView = navCon.topViewController as? MapViewController
    }
    self.startInitialPeriodWithTimeInterval(60.0)
}

func applicationDidEnterBackground(application: UIApplication!) {
    self.startInitialPeriodWithTimeInterval(15 * 60.0)
}

func startInitialPeriodWithTimeInterval(timeInterval: NSTimeInterval) {
    timer?.invalidate() // reset timer
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getFirstLocationUpdate:"), userInfo: timeInterval, repeats: false)
}

func getFirstLocationUpdate(sender: NSTimer) {
    let timeInterval = sender.userInfo as Double
    timer?.invalidate()
    mapView?.canReportLocation = true
    timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target: self, selector: Selector("waitForTimer:"), userInfo: timeInterval, repeats: true)
}

func waitForTimer(sender: NSTimer) {
    let time = sender.userInfo as Double
    locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
    finalTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: Selector("getLocationUpdate"), userInfo: nil, repeats: false)
}

func getLocationUpdate() {
    finalTimer?.invalidate()
    mapView?.canReportLocation = true
}

在mapView中(locationManager指向AppDelegate中的对象)

override func viewDidLoad() {
    super.viewDidLoad()
    var appDelegate = UIApplication.sharedApplication().delegate! as AppDelegate
    locationManager = appDelegate.locationManager!
    locationManager.delegate = self
    canReportLocation = true
}

  func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        if canReportLocation! {
            canReportLocation = false
            locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
        } else {
            //println("Ignore location update")
        }
    }

我确实写了一个使用位置服务的应用程序,应用程序必须每10秒发送位置。 而且效果很好。

只需使用“allowdeferredlocationupdatesuntiltravelled:timeout”方法,遵循苹果的文档。

我所做的是:

必需:注册后台模式更新位置。

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. 在后台使用allowdeferredlocationupdatesuntilmoved:timeout方法让应用永远运行,当应用移到后台时,你必须用新参数重新启动updatingLocation,就像这样:

- (void)applicationWillResignActive:(UIApplication *)application {
     _isBackgroundMode = YES;

    [_locationManager stopUpdatingLocation];
    [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [_locationManager setDistanceFilter:kCLDistanceFilterNone];
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [_locationManager startUpdatingLocation];
 }

3.应用程序获得updatedLocations正常与locationManager:didUpdateLocations: callback:

-(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: callback中处理数据

- (void) locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error {

     _deferringUpdates = NO;

     //do something 
}

5. 注意:我认为每次应用程序在后台/前台模式之间切换时,我们应该重置LocationManager的参数。

在iOS 8/9/10上,每5分钟更新一次后台位置,执行以下操作:

进入项目->能力->背景模式->选择位置更新 转到项目->信息->添加一个键NSLocationAlwaysUsageDescription空值(或可选的任何文本) 当你的应用程序在后台工作,并发送坐标到web服务或做任何事情,每5分钟实现如下代码。

我没有使用任何后台任务或计时器。我在我的iOS 8.1设备上测试了这段代码,我的应用程序在后台运行了几个小时。设备被锁定,代码一直正常运行。

@interface LocationManager () <CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSDate *lastTimestamp;

@end

@implementation LocationManager

+ (instancetype)sharedInstance
{
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        LocationManager *instance = sharedInstance;
        instance.locationManager = [CLLocationManager new];
        instance.locationManager.delegate = instance;
        instance.locationManager.desiredAccuracy = kCLLocationAccuracyBest; // you can use kCLLocationAccuracyHundredMeters to get better battery life
        instance.locationManager.pausesLocationUpdatesAutomatically = NO; // this is important
    });

    return sharedInstance;
}

- (void)startUpdatingLocation
{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if (status == kCLAuthorizationStatusDenied)
    {
        NSLog(@"Location services are disabled in settings.");
    }
    else
    {
        // for iOS 8
        if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        {
            [self.locationManager requestAlwaysAuthorization];
        }
        // for iOS 9
        if ([self.locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
        {
            [self.locationManager setAllowsBackgroundLocationUpdates:YES];
        }

        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *mostRecentLocation = locations.lastObject;
    NSLog(@"Current location: %@ %@", @(mostRecentLocation.coordinate.latitude), @(mostRecentLocation.coordinate.longitude));

    NSDate *now = [NSDate date];
    NSTimeInterval interval = self.lastTimestamp ? [now timeIntervalSinceDate:self.lastTimestamp] : 0;

    if (!self.lastTimestamp || interval >= 5 * 60)
    {
        self.lastTimestamp = now;
        NSLog(@"Sending current location to web service.");
    }
}

@end

有一个cocoapod APScheduledLocationManager,允许每n秒获得所需位置精度的后台位置更新。

let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)

存储库还包含一个用Swift 3编写的示例应用程序。

给其他做噩梦的人想想这个。我有一个简单的解决办法。

look this example from raywenderlich.com-> have sample code, this works perfectly, but unfortunately no timer during background location. this will run indefinitely. Add timer by using : -(void)applicationDidEnterBackground { [self.locationManager stopUpdatingLocation]; UIApplication* app = [UIApplication sharedApplication]; bgTask = [app beginBackgroundTaskWithExpirationHandler:^{ [app endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }]; self.timer = [NSTimer scheduledTimerWithTimeInterval:intervalBackgroundUpdate target:self.locationManager selector:@selector(startUpdatingLocation) userInfo:nil repeats:YES]; } Just don't forget to add "App registers for location updates" in info.plist.