在应用程序进入后台后每n分钟获取用户位置。

时间:2020-12-10 01:26:34

I am trying to implement the suggestions given in this post.

我正在努力落实这篇文章中提出的建议。

Unfortunately the steps are not clear to me. I tried implementing those suggestions, but the backgroundTimeRemaining continues to decrease even after I start and stop locationServices. This is how I developed it:

不幸的是,我不清楚这些步骤。我尝试执行这些建议,但即使在启动和停止locationServices之后,后台时间管理仍然继续减少。我就是这样发展的:

- (void)applicationDidEnterBackground:(UIApplication *)application {

    UIApplication*    app = [UIApplication sharedApplication];

    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // Do the work associated with the task.
        self.timer = nil;
        [self initTimer];

    });
}

initTimer:

initTimer:

- (void)initTimer {

    // Create the location manager if this object does not
    // already have one.
    if (nil == self.locationManager)
        self.locationManager = [[CLLocationManager alloc] init];

    self.locationManager.delegate = self;
    [self.locationManager startMonitoringSignificantLocationChanges];

    if (self.timer == nil) {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
                                              target:self
                                            selector:@selector(checkUpdates:)
                                            userInfo:nil
                                             repeats:YES];
    }
}

checkUpdates:

checkUpdates:

- (void)checkUpdates:(NSTimer *)timer{
    UIApplication*    app = [UIApplication sharedApplication];
    double remaining = app.backgroundTimeRemaining;
    if(remaining < 580.0) {
        [self.locationManager startUpdatingLocation]; 
        [self.locationManager stopUpdatingLocation]; 
        [self.locationManager startMonitoringSignificantLocationChanges];
    }
    DbgLog(@"Reminaing %f", app.backgroundTimeRemaining);
}

Does anyone have a suggestion on what might be wrong in my code? Both initTimer and checkUpdates are being called, but only during for the background execution time (+- 10 Mins). I want the app to update the location every n minutes "forever".

有人对我的代码有什么问题有什么建议吗?initTimer和checkUpdates都被调用,但只在后台执行时间(+- 10分钟)期间调用。我想让这个应用每n分钟更新一次位置“永远”。

My app's UIBackgroundModes is set to location.

我的应用程序的uibackgroundmode被设置为location。

UPDATE:

更新:

I am now resetting the timer on didUpdateToLocation and didFailWithError. But still the backgroundTimeRemaining keeps decreasing:

我现在在didUpdateToLocation和didFailWithError上重新设置计时器。但背景时间效应仍在不断减少:

- (void)locationManager:(CLLocationManager *)manager 
didUpdateToLocation:(CLLocation *)newLocation 
       fromLocation:(CLLocation *)oldLocation {

NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);

UIApplication*    app = [UIApplication sharedApplication];

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    [app endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

    [self initTimer];

});
}

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

[self.locationManager stopUpdatingLocation]; 
UIApplication*    app = [UIApplication sharedApplication];

bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
    [app endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
}];

// Start the long-running task and return immediately.
[self initTimer];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.

});

}

I am also invalidating the timer:

我也在废除计时器:

- (void)checkUpdates:(NSTimer *)timer{
UIApplication*    app = [UIApplication sharedApplication];
double remaining = app.backgroundTimeRemaining;
if(remaining < 580.0 && remaining > 570.0) {
    [self.timer invalidate];
    self.timer = nil;
    [self.locationManager startUpdatingLocation]; 
    [self.locationManager stopUpdatingLocation]; 
}
DbgLog(@"*************************Checking for updates!!!!!!!!!!! Reminaing %f", app.backgroundTimeRemaining);
}

4 个解决方案

#1


10  

To anyone else having a nightmare of a time trying to figure this one out, I have a simple solution.

对于任何想要解决这个问题的人来说,我有一个简单的解决方案。

  1. Study the example from raywenderlich.com. The sample code works perfectly, but unfortunately there's no timer during background location. This will run indefinitely.
  2. 研究来自raywenderlich.com的例子。示例代码工作得很好,但不幸的是,在后台位置没有计时器。这将运行下去。
  3. Add timer by using this code snippet:

    使用此代码片段添加计时器:

    -(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];
    }
    
  4. Just don't forget to add "App registers for location updates" in info.plist.

    只是别忘了在info.plist中添加“位置更新的应用程序寄存器”。

#2


9  

After some days trying all possible solutions I was finally able to make it work. Here is what was wrong in my solution:

经过几天的努力,我终于能够成功。以下是我的解决方案中存在的问题:

  • I need to setup 2 UIBackgroundTaskIdentifiers, one for the timer, and another one for the locationManager
  • 我需要设置两个uibackgroundtaskidentifier,一个用于计时器,另一个用于locationManager

On my solution, just add:

我的解决方案是:

UIApplication *app = [UIApplication sharedApplication];

bgTask2 = [app beginBackgroundTaskWithExpirationHandler:^{ 
[app endBackgroundTask:bgTask2]; 
bgTask2 = UIBackgroundTaskInvalid; }];

self.locationManager.delegate = self; 
[self.locationManager startUpdatingLocation]; 

#3


4  

Once you're in the background you can only have 10 minutes of additional time. There is no way to extend that. You should use the location background services instead.

一旦你在后台,你只能有10分钟的额外时间。这是无法延伸的。您应该使用位置后台服务。

#4


0  

Working Code(Entire Stepwise Code) Modified scurioni's code

工作代码(完整的逐步代码)修改了scurioni的代码

Step 1

步骤1

  • Go to project -> Capabilities -> Background Modes -> select Location updates.
  • 去项目->功能->后台模式->选择位置更新。
  • Go to Project -> Info -> add a key NSLocationAlwaysUsageDescription with an optional string.
  • 转到Project -> Info ->,添加一个带有可选字符串的关键NSLocationAlwaysUsageDescription。

Step 2

步骤2

Add this code to AppDelegate.m

将此代码添加到AppDelegate.m中

@interface AppDelegate ()<CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSTimer *timer;
@end

Step 3 Add This Code in to applicationDidEnterBackground method in AppDelegate.m

步骤3将此代码添加到AppDelegate.m中的applicationDidEnterBackground方法中

    - (void)applicationDidEnterBackground:(UIApplication *)application {
        UIApplication *app = [UIApplication sharedApplication];
        __block UIBackgroundTaskIdentifier bgTaskId =
        [app beginBackgroundTaskWithExpirationHandler:^{
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        }];

        dispatch_async( dispatch_get_main_queue(), ^{
            self.timer = nil;
            [self initTimer];
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        });
    }

- (void)initTimer {
    if (nil == self.locationManager)
        self.locationManager = [[CLLocationManager alloc] init];

    self.locationManager.delegate = self;
    [self.locationManager requestAlwaysAuthorization];
    [self.locationManager startMonitoringSignificantLocationChanges];
    if (self.timer == nil) {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
                                                      target:self
                                                    selector:@selector(checkUpdates:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
}

- (void)checkUpdates:(NSTimer *)timer{
    UIApplication *app = [UIApplication sharedApplication];
    double remaining = app.backgroundTimeRemaining;
    if(remaining < 580.0) {
        [self.locationManager startUpdatingLocation];
        [self.locationManager stopUpdatingLocation];
        [self.locationManager startMonitoringSignificantLocationChanges];
    }
}

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
    NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
    [self updateLocationWithLatitude:[newLocation coordinate].latitude andLongitude:[newLocation coordinate].longitude];
    UIApplication*    app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self initTimer];
    });
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [self.locationManager stopUpdatingLocation];
    UIApplication *app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    [self initTimer];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Do the work associated with the task
    });
}

-(void)updateLocationWithLatitude:(CLLocationDegrees)latitude
                     andLongitude:(CLLocationDegrees)longitude{
//Here you can update your web service or back end with new latitude and longitude
}

#1


10  

To anyone else having a nightmare of a time trying to figure this one out, I have a simple solution.

对于任何想要解决这个问题的人来说,我有一个简单的解决方案。

  1. Study the example from raywenderlich.com. The sample code works perfectly, but unfortunately there's no timer during background location. This will run indefinitely.
  2. 研究来自raywenderlich.com的例子。示例代码工作得很好,但不幸的是,在后台位置没有计时器。这将运行下去。
  3. Add timer by using this code snippet:

    使用此代码片段添加计时器:

    -(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];
    }
    
  4. Just don't forget to add "App registers for location updates" in info.plist.

    只是别忘了在info.plist中添加“位置更新的应用程序寄存器”。

#2


9  

After some days trying all possible solutions I was finally able to make it work. Here is what was wrong in my solution:

经过几天的努力,我终于能够成功。以下是我的解决方案中存在的问题:

  • I need to setup 2 UIBackgroundTaskIdentifiers, one for the timer, and another one for the locationManager
  • 我需要设置两个uibackgroundtaskidentifier,一个用于计时器,另一个用于locationManager

On my solution, just add:

我的解决方案是:

UIApplication *app = [UIApplication sharedApplication];

bgTask2 = [app beginBackgroundTaskWithExpirationHandler:^{ 
[app endBackgroundTask:bgTask2]; 
bgTask2 = UIBackgroundTaskInvalid; }];

self.locationManager.delegate = self; 
[self.locationManager startUpdatingLocation]; 

#3


4  

Once you're in the background you can only have 10 minutes of additional time. There is no way to extend that. You should use the location background services instead.

一旦你在后台,你只能有10分钟的额外时间。这是无法延伸的。您应该使用位置后台服务。

#4


0  

Working Code(Entire Stepwise Code) Modified scurioni's code

工作代码(完整的逐步代码)修改了scurioni的代码

Step 1

步骤1

  • Go to project -> Capabilities -> Background Modes -> select Location updates.
  • 去项目->功能->后台模式->选择位置更新。
  • Go to Project -> Info -> add a key NSLocationAlwaysUsageDescription with an optional string.
  • 转到Project -> Info ->,添加一个带有可选字符串的关键NSLocationAlwaysUsageDescription。

Step 2

步骤2

Add this code to AppDelegate.m

将此代码添加到AppDelegate.m中

@interface AppDelegate ()<CLLocationManagerDelegate>
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) NSTimer *timer;
@end

Step 3 Add This Code in to applicationDidEnterBackground method in AppDelegate.m

步骤3将此代码添加到AppDelegate.m中的applicationDidEnterBackground方法中

    - (void)applicationDidEnterBackground:(UIApplication *)application {
        UIApplication *app = [UIApplication sharedApplication];
        __block UIBackgroundTaskIdentifier bgTaskId =
        [app beginBackgroundTaskWithExpirationHandler:^{
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        }];

        dispatch_async( dispatch_get_main_queue(), ^{
            self.timer = nil;
            [self initTimer];
            [app endBackgroundTask:bgTaskId];
            bgTaskId = UIBackgroundTaskInvalid;
        });
    }

- (void)initTimer {
    if (nil == self.locationManager)
        self.locationManager = [[CLLocationManager alloc] init];

    self.locationManager.delegate = self;
    [self.locationManager requestAlwaysAuthorization];
    [self.locationManager startMonitoringSignificantLocationChanges];
    if (self.timer == nil) {
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.3
                                                      target:self
                                                    selector:@selector(checkUpdates:)
                                                    userInfo:nil
                                                     repeats:YES];
    }
}

- (void)checkUpdates:(NSTimer *)timer{
    UIApplication *app = [UIApplication sharedApplication];
    double remaining = app.backgroundTimeRemaining;
    if(remaining < 580.0) {
        [self.locationManager startUpdatingLocation];
        [self.locationManager stopUpdatingLocation];
        [self.locationManager startMonitoringSignificantLocationChanges];
    }
}

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation {
    NSLog(@"Did Update Location = %f / %f", [newLocation coordinate].latitude, [newLocation coordinate].longitude);
    [self updateLocationWithLatitude:[newLocation coordinate].latitude andLongitude:[newLocation coordinate].longitude];
    UIApplication*    app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self initTimer];
    });
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    [self.locationManager stopUpdatingLocation];
    UIApplication *app = [UIApplication sharedApplication];
    __block UIBackgroundTaskIdentifier bgTask =
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];
    [self initTimer];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // Do the work associated with the task
    });
}

-(void)updateLocationWithLatitude:(CLLocationDegrees)latitude
                     andLongitude:(CLLocationDegrees)longitude{
//Here you can update your web service or back end with new latitude and longitude
}