ios 应用程序进入后台后每 n 分钟获取一次用户位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10235203/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 17:57:39  来源:igfitidea点击:

Getting user location every n minutes after app goes to background

iosobjective-ciphonebackgroundlocationmanager

提问by scurioni

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 之后,backgroundTimeRemaining 仍在继续减少。我是这样开发的:

- (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:

初始化定时器:

- (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:

检查更新:

- (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.

我的应用程序的 UIBackgroundModes 设置为位置。

UPDATE:

更新:

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

我现在正在重置 didUpdateToLocation 和 didFailWithError 上的计时器。但 backgroundTimeRemaining 仍然不断减少:

- (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);
}

采纳答案by scurioni

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 2UIBackgroundTaskIdentifiers, one for the timer, and another one for the locationManager
  • 我需要设置2 个UIBackgroundTaskIdentifiers,一个用于计时器,另一个用于 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]; 

回答by HelmiB

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

  1. 研究来自raywenderlich.com的示例。示例代码运行良好,但不幸的是在后台定位期间没有计时器。这将无限期运行。
  2. 使用以下代码片段添加计时器:

    -(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];
    }
    
  3. 只是不要忘记在 info.plist 中添加“应用程序注册位置更新”。

回答by jsd

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 分钟的额外时间。没有办法扩展它。您应该改用位置后台服务。

回答by Hari R Krishna

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

工作代码(Entire Stepwise 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 3Add This Code in to applicationDidEnterBackgroundmethod 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
}