iOS后台定位未发送HTTP请求

12
我的应用程序需要在后台跟踪用户的位置,但无法发送“get”请求。当应用程序回到前台时,http请求会立即发送。我使用RestKit处理所有网络请求,并遵循这个教程设置我的后台位置服务。在我的applicationDidEnterBackground方法中。
-(void)applicationDidEnterBackground:(UIApplication *)application
{
    self.bgLocationManager = [[CLLocationManager alloc] init];
    self.bgLocationManager.delegate = self;
    [self.bgLocationManager startMonitoringSignificantLocationChanges];
    NSLog(@"Entered Background");
}

我在applicationDidBecomeActive委托中停止监控显著位置变化。

这是我的locationManager委托,我在其中接受新的更新位置并发送到我的服务器。

-(void) locationManager:(CLLocationManager *)manager 
    didUpdateToLocation:(CLLocation *)newLocation 
           fromLocation:(CLLocation *)oldLocation
{
    NSLog(@"I am in the background");
    bgTask = [[UIApplication sharedApplication]
                beginBackgroundTaskWithExpirationHandler:
                ^{
                      [[UIApplication sharedApplication] endBackgroundTask:bgTask];
                 }];
                 // ANY CODE WE PUT HERE IS OUR BACKGROUND TASK

    NSString *currentLatitude = [[NSString alloc]
                                  initWithFormat:@"%g",
                                  newLocation.coordinate.latitude];
    NSString *currentLongitude = [[NSString alloc]
                                   initWithFormat:@"%g",
                                   newLocation.coordinate.longitude];
    NSString *webToken = [[NSUserDefaults standardUserDefaults] stringForKey:@"userWebToken"];
    NSLog(@"I am in the bgTask, my lat %@", currentLatitude);

    NSDictionary *queryParams;
    queryParams = [NSDictionary dictionaryWithObjectsAndKeys:webToken, @"auth_token",  currentLongitude, @"lng", currentLatitude, @"lat", nil];
    RKRequest* request = [[RKClient sharedClient] post:@"/api/locations/background_update" params:queryParams delegate:self];
    //default is RKRequestBackgroundPolicyNone
    request.backgroundPolicy = RKRequestBackgroundPolicyContinue;

    // AFTER ALL THE UPDATES, close the task

    if (bgTask != UIBackgroundTaskInvalid)
    {
        [[UIApplication sharedApplication] endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }
}

网络请求按计划进行,但不会在后台调用。我需要采取其他措施吗?在我的info.plist中,我有所需的后台模式键和位置服务作为值。
编辑
我还参考了这个SO答案。我在didUpdateToLocation调用中放置了一些日志测试,它们都被调用,但'get'请求没有被发送。相反,当我最终将应用程序启动到前台时,它发送了所有构建的网络请求(超过10个)。
编辑(2)
我向我的请求添加了RKRequestBackgroundPolicyContinue,但结果没有改变。(如您可以在此处看到的Restkit中的后台上传/下载)。我看到Restkit初始化主机,但直到应用程序变为活动状态才发送请求。
答案
RestKit必须正在执行某些在后台被禁止的操作。使用NSURLRequest完美地工作。
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.example.com/api/locations/background_update"]];
[urlRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
[urlRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[urlRequest setHTTPMethod:@"POST"];
[urlRequest setHTTPBody:jsonData];

NSHTTPURLResponse  *response = nil;
[NSURLConnection sendSynchronousRequest:urlRequest
                      returningResponse:&response
                                  error:&error];

由于没有UI会被后台任务打扰,因此使用同步请求是可以的。


你尝试过用同步的原生 NSURLConnection 替换 RestKit 调用吗? - dklt
我同意dklt的问题,因为调用[[RKClient sharedClient] post:params:delegate:]很可能会导致一些在后台GCD队列中发生的事情,这些事情可能不被你的backgroundTask括号所覆盖...而同步调用以获取数据可能有助于在后台任务中发送请求。是的,RKRequestBackgroundPolicyContinue可能旨在解决此问题,但实际上直到调用post:params:delegate:之后才设置它,那时可能已经太晚了。 - john.k.doe
@dklt,我已经通过NSURLConnection请求使其正常工作了,感谢您的建议。如果您提供答案,我会接受它。 - Kyle C
3个回答

3

重新将原始建议转化为答案

你尝试过使用标准同步的NSURLConnection替换您的restKit调用吗? - dklt Sep 20


2

我正在使用和你完全相同的代码,并且在RestKit中可以正常工作。唯一让它能够工作的方法是创建一个同步请求(在这种情况下,异步请求并不是很有意义!)。请检查此代码并告诉我们它是否有效:

// REMEMBER. We are running in the background if this is being executed.
// We can't assume normal network access.
// bgTask is defined as an instance variable of type UIBackgroundTaskIdentifier

// Note that the expiration handler block simply ends the task. It is important that we always
// end tasks that we have started.

_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:
           ^{
               [[UIApplication sharedApplication] endBackgroundTask:_bgTask];
           }];

// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK

// For example, I can do a series of SYNCHRONOUS network methods (we're in the background, there is
// no UI to block so synchronous is the correct approach here).

NSNumber *latNumber = [NSNumber numberWithDouble:location.coordinate.latitude];
NSNumber *lngNumber = [NSNumber numberWithDouble:location.coordinate.longitude];
NSNumber *accuracyNumber = [NSNumber numberWithDouble:location.horizontalAccuracy];
NSDictionary *params = [NSDictionary dictionaryWithKeysAndObjects:@"lat",latNumber,@"lng",lngNumber,@"accuracy",accuracyNumber, nil];
RKURL *URL = [RKURL URLWithBaseURL:[NSURL URLWithString:SERVER_URL] resourcePath:@"/user/location/update" queryParameters:params];
RKRequest *request = [RKRequest requestWithURL:URL];
request.method = RKRequestMethodGET;
NSLog(@"Sending location to the server");
RKResponse *response = [request sendSynchronously];
if (response.isFailure)
    NSLog(@"Unable to send background location, failure: %@", response.failureErrorDescription);
else {
    NSError *error = nil;
    NSDictionary *parsedBody = [response parsedBody:&error];
    if (YES == [[parsedBody objectForKey:@"result"] boolValue]){
        NSLog(@"Background location sent to server");
    }
    else {
        //Something went bad
        NSLog(@"Failed to send background location");
    }
}
// AFTER ALL THE UPDATES, close the task

if (_bgTask != UIBackgroundTaskInvalid)
{
    [[UIApplication sharedApplication] endBackgroundTask:_bgTask];
    _bgTask = UIBackgroundTaskInvalid;
}

我几乎可以确定为你的RKClient请求生成的新线程在调用后会自动终止。


-1

当你的应用程序在后台运行时,你可以完成之前进入后台时开始的HTTP请求,但是你不能发起一个新的请求。你只能在后台启动某些网络操作(voip、报刊亭)。


谢谢您的回答,我仍然看到关于这个主题的矛盾信息。请查看此前的SO答案,https://dev59.com/vm035IYBdhLWcg3wef9y - Kyle C
@KyleC 他的意思是,如果你在前台启动的HTTP请求没有在应用程序终止之前完成,那么你应该使用beginBackgroundTaskWithExpirationHandler:方法请求额外的时间来完成请求。这个方法允许在第一次进入后台时有额外的时间来完成任何你已经开始并想要完成的任务,但是在这种状态下你不能开始新的HTTP请求,我发现这段时间从5秒到10分钟不等。 - Shizam
2
我理解了这部分内容,我知道方法beginBackgroundTaskWithExpirationHandler:用于在应用进入后台时完成较长时间运行的任务。然而,他的回答表明,你的应用程序会在后台状态下被重大位置更改唤醒,并且可以使用beginBackgroundTaskWithExpirationHandler:在后台完成所有http请求。 - Kyle C
哦,太酷了!我没想到那个事件可以做到这个。 - Shizam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接