2012-09-17 20 views
12

Mi aplicación necesita rastrear la ubicación de los usuarios en segundo plano, pero no envía una solicitud 'get'. La solicitud http se envía inmediatamente cuando la aplicación aparece en primer plano. Estoy usando RestKit para todas mis solicitudes de red y seguí this tutorial para configurar el servicio de ubicaciones en segundo plano. En mi applicationDidEnterBackgroundAntecedentes iOS La ubicación no envía la solicitud HTTP

-(void)applicationDidEnterBackground:(UIApplication *)application 
{ 
    self.bgLocationManager = [[CLLocationManager alloc] init]; 
    self.bgLocationManager.delegate = self; 
    [self.bgLocationManager startMonitoringSignificantLocationChanges]; 
    NSLog(@"Entered Background"); 
} 

y yo stopMonitoringSignificantLocationChange en mi delegado applicationDidBecomeActive

Ésta es mi locationManager delegado donde Acepto la nueva ubicación actualizada y enviar a mi servidor

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

La red solicita obras como estaba previsto, pero no se llamará en segundo plano. ¿Hay algún paso adicional que necesito? En mi info.plist tengo la clave de modos de fondo requeridos y los servicios de ubicación como el valor.

EDITAR

También se hace referencia a this past SO answer. Realicé algunas pruebas con la colocación de registros a lo largo de la llamada didUpdateToLocation y se llamaron todos, pero no se envió la solicitud 'get'. En cambio, cuando finalmente lancé la aplicación al primer plano, envió todas las solicitudes de red integradas (más de 10).

EDIT (2) He añadido RKRequestBackgroundPolicyContinue a mi pedido, pero no ha cambiado mis resultados. (Como puede ver here en la carga/descarga de fondo para restkit). Veo que Restkit inicializa el host pero no envía la solicitud hasta que la aplicación se active.

RESPUESTA

RestKit debe estar haciendo algo que está prohibido en el fondo. Usar un NSURLRequest funciona perfectamente.

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]; 

Está bien usar una petición síncrona ya que no hay interfaz de usuario para interrumpir con las tareas de fondo

+0

¿Tiene el intento de reemplazar sus llamadas restKit con un NSURLConnection stock sincrónico? – dklt

+0

estoy de acuerdo con la pregunta de dklt, porque parece probable que la llamada a la publicación '[[RKClient sharedClient]: params: delegate:]' esté causando que algo suceda en una cola de GCD que no está cubierta por su bracketing de AntecedentesTask ... y que las llamadas sincrónicas para obtener sus datos pueden ayudar de tal manera que las solicitudes se envíen mientras está en la tarea de fondo. sí, el RKRequestBackgroundPolicyContinue puede estar destinado a encargarse de esto, pero en realidad no lo está configurando hasta después de la llamada a la publicación: params: delegate: y en ese punto, puede ser demasiado tarde. –

+0

@dklt Pude hacerlo funcionar con una solicitud de NSURLConnection, gracias por la sugerencia. Si pones una respuesta, la aceptaré. –

Respuesta

3

Recreando sugerencia original como respuesta

que su intento de reemplazar la restKit llamadas con un NSURLConnection síncrono de valores? - dklt Sep 20

-1

Cuando estás aplicación se ejecuta en el fondo se puede terminar una petición HTTP que comenzó antes de entrar el fondo pero no puede iniciar una solicitud nueva. Solo puede iniciar certain network operations mientras está en segundo plano (voip, quiosco de prensa).

+0

Gracias por su respuesta, todavía estoy viendo información conflictiva sobre este tema. Echa un vistazo a esta respuesta SO anterior, http://stackoverflow.com/questions/5394880/can-iphone-app-woken-in-background-for-significant-location-change-do-network-ac –

+0

@KyleC Lo que es es decir, si encuentra que su solicitud HTTP (que comenzó mientras estaba en primer plano) no termina antes de que su aplicación finalice, debe solicitar tiempo adicional para finalizar la solicitud utilizando el método beginBackgroundTaskWithExpirationHandler :. Este método permite más tiempo cuando ingresas por primera vez en el fondo para completar cualquier tarea que hayas comenzado y quieras terminar, pero no puedes iniciar nuevas solicitudes HTTP mientras estás en este estado. He encontrado que esta vez varía desde 5 segundos hasta más. 10 minutos. – Shizam

+2

Entiendo esa parte y conozco el método beginBackgroundTaskWithExpirationHandler: se usa para ejecutar tareas más largas para finalizar cuando la aplicación se pone en segundo plano. Sin embargo, su respuesta sugiere que su aplicación se despierta en el estado de fondo por el cambio significantLocation y puede usar beginBackgroundTaskWithExpirationHandler: para completar una solicitud http en segundo plano. –

2

Estoy usando exactamente el mismo código que tú y funciona para mí en RestKit. La única forma en que podría hacerlo funcionar es si estoy creando una solicitud sincrónica (¡no tiene mucho sentido hacerlo de forma asíncrona en este contexto de todos modos!). Por favor, compruebe el código y háganos saber si funciona:

// 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; 
} 

Estoy casi seguro de que el nuevo hilo dio lugar a su solicitud RKClient se mata automáticamente después de que la invoca.

Cuestiones relacionadas