Mise à jour périodique de l'emplacement de l'iOS

je suis en train d'écrire une application qui nécessite des mises à jour de la position de fond avec haute précision et basse fréquence . La solution semble être une tâche NSTimer de fond qui démarre les mises à jour du gestionnaire d'emplacement, qui s'arrête immédiatement. Cette question a déjà été posée:

Comment puis-je obtenir une mise à jour de l'emplacement de fond toutes les n minutes dans mon application iOS?

Obtenir l'emplacement de l'utilisateur toutes les n minutes après application va à l'arrière-plan

iOS Pas le type d'arrière-plan de localisation de la minuterie de l'émission

iOS longue course de fond de la minuterie avec "location" de fond en mode

iOS à temps plein d'arrière-plan-service basé sur la localisation

mais je dois encore obtenir un exemple minimum de travail. Après avoir essayé toutes les permutations des réponses acceptées ci-dessus, j'ai rassemblé un point de départ. Entrée dans le fond:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"ending background task");
        [[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
        self.bgTask = UIBackgroundTaskInvalid;
    }];


    self.timer = [NSTimer scheduledTimerWithTimeInterval:60
                                                  target:self.locationManager
                                                selector:@selector(startUpdatingLocation)
                                                userInfo:nil
                                                 repeats:YES];
}

et la méthode du délégué:

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

    NSLog(@"%@", newLocation);

    NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
    [self.locationManager stopUpdatingLocation];

}

le comportement actuel est que le backgroundTimeRemaining décroit de 180 secondes à zéro (pendant la localisation de la journalisation), puis le gestionnaire d'expiration exécute et aucune autre mise à jour de la localisation n'est générée. Comment modifier le code ci-dessus en ordre de recevoir mises à jour périodiques de l'emplacement dans l'arrière-plan indéfiniment ?

Update :Je cible iOS 7 et il semble y avoir des preuves que les tâches de fond se comportent différemment:

de l'Endroit de Départ dans le Gestionnaire de iOS 7 à partir de tâche de fond

84
demandé sur Honey 2013-09-27 08:44:30

8 réponses

il semble que stopUpdatingLocation est ce qui déclenche le chronomètre de fond de chien de garde, donc je l'ai remplacé dans didUpdateLocation par:

[self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
[self.locationManager setDistanceFilter:99999];

, ce qui semble réduire la puissance du GPS. Le sélecteur pour le fond NSTimer devient alors:

- (void) changeAccuracy {
    [self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}

tout ce que je fais est de changer périodiquement la précision pour obtenir une coordonnée de haute précision toutes les quelques minutes et parce que le locationManager n'a pas été arrêté, backgroundTimeRemaining reste à sa valeur maximale. Cela a réduit la consommation de batterie de ~10% par heure (avec constante kCLLocationAccuracyBest en arrière-plan) à ~2% par heure sur mon appareil.

61
répondu pcoving 2013-10-29 17:16:27

j'ai écrit une application en utilisant les services D'emplacement, l'application doit envoyer l'emplacement tous les 10s. Et ça a très bien fonctionné.

il suffit D'utiliser la " allowDeferredLocationUpdatesUntiltraveled:timeout " méthode, suivant Doc D'Apple.

les Étapes sont comme suit:

Requis: Enregistrer le mode arrière-plan pour l'Emplacement de mise à jour.

1. créer LocationManger et startUpdatingLocation, avec précision et distance filtered que tout ce que vous voulez:

-(void) initLocationManager    
{
    // Create the manager object
    self.locationManager = [[[CLLocationManager alloc] init] autorelease];
    _locationManager.delegate = self;
    // This is the most important property to set for the manager. It ultimately determines how the manager will
    // attempt to acquire location and thus, the amount of power that will be consumed.
    _locationManager.desiredAccuracy = 45;
    _locationManager.distanceFilter = 100;
    // Once configured, the location manager must be "started".
    [_locationManager startUpdatingLocation];
}

2. pour maintenir le fonctionnement de l'appli pour toujours en utilisant la méthode "allowDeferredLocationUpdatesUntiltraveled:timeout" en arrière-plan, vous devez redémarrer la mise à jour de la localisationavec un nouveau paramètre lorsque l'appli passe en arrière-plan, comme ceci:

- (void)applicationWillResignActive:(UIApplication *)application {
     _isBackgroundMode = YES;

    [_locationManager stopUpdatingLocation];
    [_locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [_locationManager setDistanceFilter:kCLDistanceFilterNone];
    _locationManager.pausesLocationUpdatesAutomatically = NO;
    _locationManager.activityType = CLActivityTypeAutomotiveNavigation;
    [_locationManager startUpdatingLocation];
 }

3. Application updatedLocations comme d'habitude avec "locationManager:didUpdateLocations:" rappel:

-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
//  store data
    CLLocation *newLocation = [locations lastObject];
    self.userLocation = newLocation;

   //tell the centralManager that you want to deferred this updatedLocation
    if (_isBackgroundMode && !_deferringUpdates)
    {
        _deferringUpdates = YES;
        [self.locationManager allowDeferredLocationUpdatesUntilTraveled:CLLocationDistanceMax timeout:10];
    }
}

4. mais vous devez traiter les données dans alors " locationManager: didFinishDeferredUpdatesWithError: "callback for your purpose

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

     _deferringUpdates = NO;

     //do something 
}

5. NOTE: je pense que nous devrions Réinitialiser les paramètres de LocationManager chaque fois que l'application commute entre le mode arrière-plan/forground.

44
répondu samthui7 2016-07-27 07:05:06
  1. si vous avez le UIBackgroundModes dans votre liste avec la touche location alors vous n'avez pas besoin d'utiliser la méthode beginBackgroundTaskWithExpirationHandler . C'est redondant. Vous l'utilisez aussi incorrectement (voir ici ) mais c'est discutable puisque votre plist est réglé.

  2. avec UIBackgroundModes location dans le plist l'application continuera à fonctionner dans l'arrière-plan indéfiniment seulement aussi longtemps que CLLocationManger est en cours d'exécution. Si vous appelez stopUpdatingLocation alors qu'en arrière-plan l'application s'arrêtera et ne recommencera pas.

    peut - être pourriez-vous appeler beginBackgroundTaskWithExpirationHandler juste avant d'appeler stopUpdatingLocation et ensuite après avoir appelé startUpdatingLocation vous pourriez appeler le endBackgroundTask pour garder l'arrière-plan pendant que le GPS est arrêté, mais je n'ai jamais essayé cela-c'est juste une idée.

    une autre option (que je n'ai pas essayée) est de garder le gestionnaire d'emplacement en cours d'exécution tout en arrière-plan mais une fois que vous obtenez une localisation précise change la propriété desiredAccuracy à 1000m ou plus pour permettre à la puce GPS de s'éteindre (pour économiser la batterie). Puis 10 minutes plus tard quand vous avez besoin d'une autre mise à jour de localisation, changez le desiredAccuracy de nouveau à 100m pour allumer le GPS jusqu'à ce que vous obtenez une localisation précise, je répète.

  3. lorsque vous appelez startUpdatingLocation sur le gestionnaire d'emplacement, vous devez lui donner du temps pour obtenir un poste. Vous ne devez pas immédiatement appeler stopUpdatingLocation . Nous laisser tourner pendant un maximum de 10 secondes ou jusqu'à ce que nous obtenions un emplacement de haute précision non caché.

  4. vous devez filtrer les emplacements de cache et vérifier la précision de l'emplacement que vous obtenez pour s'assurer qu'il répond à votre précision minimale requise (voir ici ). La première mise à jour que vous obtenez peut-être 10 minutes ou 10 jours. La première précision que vous obtenez peut être 3000m.

  5. considérer l'utilisation des IPA de changement d'emplacement significatif à la place. Une fois que vous obtenez la notification de changement significatif, vous pouvez démarrer CLLocationManager pendant quelques secondes pour obtenir une position de haute précision. Je ne suis pas sûr, je n'ai jamais utilisé les services de changement de localisation.

32
répondu progrmr 2017-05-23 12:34:33

Lorsqu'il est temps de commencer le service de localisation et d'arrêter la tâche de fond, la tâche de fond doit être arrêtée avec un retard (1 seconde devrait suffire). Sinon, le service de localisation ne commencera pas. Le service de localisation doit également être laissé allumé pendant quelques secondes (par exemple 3 secondes).

il y a un cocoapod APScheduledLocationManager qui permet d'obtenir des mises à jour de l'emplacement de fond chaque n secondes avec l'emplacement désiré exactitude.

let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)

le dépôt contient également un exemple d'application écrit dans Swift 3.

8
répondu sash 2016-10-29 14:37:41

pourquoi ne pas essayer avec L'API startMonitoringSignificantLocationChanges: ? Il tire certainement avec moins de fréquence et la précision est assez bonne. En outre, il a beaucoup plus d'avantages que l'utilisation d'autres API locationManager .

beaucoup plus concernant cette API a déjà été discuté sur ce lien

1
répondu thatzprem 2017-05-23 12:02:40

vous devez ajouter le mode de mise à jour de l'emplacement dans votre application en ajoutant la touche suivante dans vos informations.plist.

<key>UIBackgroundModes</key>
<array>
    <string>location</string>
</array>
La méthode

didUpdateToLocation sera appelée (même si votre application est en arrière-plan). Vous pouvez effectuer n'importe quoi sur les bases de cette méthode appel

1
répondu aahsanali 2013-09-27 05:28:01

ce projet servira à gérer l'emplacement en arrière-plan.

LOCATION BACKGROUND UPDATER

se gérera automatiquement si app entre en arrière-plan et doit envoyer l'appel réseau pour mettre à jour l'emplacement.

0
répondu Esha 2017-01-27 18:34:47

après iOS 8, Il y a plusieurs changements dans le fetch de background lié au cadre de CoreLocation et les mises à jour faites par apple.

1) Apple introduit la demande D'autorisation permanente pour récupérer la mise à jour de l'emplacement dans l'application.

2) Apple introduire backgroundLocationupdates pour aller chercher de l'emplacement de l'arrière-plan dans iOS.

pour récupérer l'emplacement en arrière-plan vous devez activer la mise à jour de L'emplacement dans les capacités de Xcode.

//
//  ViewController.swift
//  DemoStackLocation
//
//  Created by iOS Test User on 02/01/18.
//  Copyright © 2018 iOS Test User. All rights reserved.
//

import UIKit
import CoreLocation

class ViewController: UIViewController {

    var locationManager: CLLocationManager = CLLocationManager()
    override func viewDidLoad() {
        super.viewDidLoad()
        startLocationManager()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    internal func startLocationManager(){
        locationManager.requestAlwaysAuthorization()
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.startUpdatingLocation()
    }

}

extension ViewController : CLLocationManagerDelegate {

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
        let location = locations[0] as CLLocation
        print(location.coordinate.latitude) // prints user's latitude
        print(location.coordinate.longitude) //will print user's longitude
        print(location.speed) //will print user's speed
    }

}
0
répondu Tech 2018-01-02 12:28:08