2012-09-17 26 views
12

Moja aplikacja musi śledzić lokalizację użytkowników w tle, ale nie może wysłać żądania "pobierz". Żądanie http zostanie wysłane natychmiast, gdy aplikacja znajdzie się na pierwszym planie. Korzystam z narzędzia RestKit w przypadku wszystkich żądań sieci i wykonałem polecenie this tutorial, aby skonfigurować usługę lokalizacji tła. W moim applicationDidEnterBackgroundLokalizacja w tle iOS Lokalizacja nie wysyłająca żądania http

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

i stopMonitoringSignificantLocationChange w moim applicationDidBecomeActive delegata

To mój locationManager delegat gdzie mogę zaakceptować nową zaktualizowaną lokalizację i wysłać do mojego serwera

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

sieć żąda prace zgodnie z planem, ale nie zostanie wywołany w tle. Czy są jakieś dodatkowe kroki, których potrzebuję? W moim pliku info.plist mam klucz trybu Wymagane tło i usługi lokalizacyjne jako wartość.

EDIT

ja również określane this past SO answer. Przeprowadziłem kilka testów z wprowadzaniem dzienników do wywołania didUpdateToLocation i wszystkie były wywoływane, ale żądanie "get" nie zostało wysłane. Zamiast tego, kiedy w końcu uruchomiłem aplikację na pierwszym planie, wysłano wszystkie utworzone żądania sieciowe (ponad 10).

EDYCJA (2) Dodałem RKRequestBackgroundPolicyContinue do mojej prośby, ale nie zmieniło to moich wyników. (Jak widać here w tle upload/download dla restkit). Widzę, że Restkit inicjuje hosta, ale nie wysyła żądania, dopóki aplikacja nie stanie się aktywna.

ODPOWIEDŹ

RestKit należy robić coś, co jest zakazane w tle. Korzystanie z NSURLRequest działa idealnie.

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

Jest dobrze, aby używać synchroniczne żądania, ponieważ nie ma UI zakłócić zadania tła

+0

Czy twoja próba zastąpienia połączeń restKit za pomocą synchronicznego NSURLConnection? – dklt

+0

Zgadzam się z pytaniem dklt, ponieważ wydaje się prawdopodobne, że wywołanie "[[RKClient sharedClient] post: params: delegate:]' powoduje coś, co może się zdarzyć w kolejce GCD za kulisami, która nie jest objęta Twoje bracketing funkcji backgroundTask ... i synchroniczne wywołania, aby uzyskać dane, mogą pomóc w wysłaniu żądań w tle. tak, RKRequestBackgroundPolicyContinue może mieć na celu zajęcie się tą kwestią, ale tak naprawdę nie ustawiasz jej tak długo, aż po wezwaniu do wysłania: params: delegate:, i do tego momentu może być za późno. –

+0

@dklt Udało mi się uzyskać to działając z prośbą NSURLConnection, dzięki za sugestię. Jeśli umieścisz odpowiedź, zaakceptuję ją. –

Odpowiedz

3

Ponowne tworzenie oryginalnego sugestię jako odpowiedź

Wyraź swoją spróbować wymienić swoje restKit apeluje o stanie synchronicznym NSURLConnection? - dklt Sep 20

-1

Kiedy jesteś aplikacja działa w tle można zakończyć żądania HTTP została uruchomiona przed wejściem w tle, ale nie można zainicjować nowego żądania: new. Możesz inicjować tylko certain network operations będąc w tle (VoIP, Kiosk).

+0

Dziękuję za odpowiedź, nadal widzę sprzeczne informacje na ten temat. Sprawdź tę przeszłą odpowiedź SO, http://stackoverflow.com/questions/5394880/can-iphone-app-woken-in-background-for-significant-location-change-do-network-ac –

+0

@KyleC Co to jest mówiąc, że jeśli znajdziesz zapytanie HTTP (które rozpoczęło się na pierwszym planie) nie zakończy się przed przerwaniem aplikacji, powinieneś poprosić o dodatkowy czas na zakończenie żądania za pomocą metody beginBackgroundTaskWithExpirationHandler:. Ta metoda pozwala na dodatkowy czas, kiedy po raz pierwszy wchodzisz w tło, aby ukończyć wszystkie rozpoczęte zadania i chcesz zakończyć, ale nie możesz rozpocząć nowych żądań HTTP, gdy znajduję się w tym stanie, znalazłem ten czas w zasięgu od 5 sekund do ponad 10 minut. – Shizam

+2

Rozumiem tę część i znam metodę beginBackgroundTaskWithExpirationHandler: jest używana do wykonywania dłuższych zadań, aby zakończyć działanie aplikacji w tle. Jednak jego odpowiedź sugeruje, że twoja aplikacja jest budzona w tle przez zmianę znaczącej lokalizacji i może używać instrukcji beginBackgroundTaskWithExpirationHandler: do wykonania żądania http w tle. –

2

Używam dokładnie tego samego kodu co Ty i działa dla mnie w RestKit. Jedynym sposobem, w jaki mogę to zrobić, jest utworzenie synchronicznej prośby (nie ma sensu robić tego asynchronicznie w tym kontekście!). Proszę sprawdzić ten kod i daj nam znać, czy to działa:

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

Jestem prawie pewien, że nowy wątek zrodził na żądanie RKClient zostaje automatycznie zabity po wywołaniu go.