J'écris une application qui nécessite des mises à jour d'emplacement en arrière-plan avec haute précision et basse fréquence. La solution semble être une tâche NSTimer d'arrière-plan qui lance les mises à jour du gestionnaire d'emplacement, qui s'arrête immédiatement. Cette question a déjà été posée:
iOS Pas le problème typique du minuteur de suivi de l'emplacement en arrière-plan
Minuterie de fond iOS longue durée avec mode arrière-plan "emplacement"
Service d'arrière-plan iOS à temps plein basé sur le suivi de la localisation
mais je n'ai pas encore obtenu un exemple minimum fonctionnel. Après avoir essayé toutes les permutations des réponses acceptées ci-dessus, j'ai établi un point de départ. Saisie du 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 déléguée:
- (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 la variable backgroundTimeRemaining
diminue de 180 secondes à zéro (lors de la journalisation de l'emplacement), puis que le gestionnaire d'expiration est exécuté et qu'aucune autre mise à jour d'emplacement n'est générée. Comment modifier le code ci-dessus afin de recevoir indéfiniment des mises à jour d'emplacement périodiques en arrière-plan?
Update: je cible iOS 7 et certains éléments semblent indiquer que les tâches en arrière-plan se comportent différemment:
Démarrez Location Manager dans iOS 7 à partir de la tâche en arrière-plan
Il semble que stopUpdatingLocation
est ce qui déclenche le chronomètre de surveillance en arrière-plan. Je l'ai donc remplacé dans didUpdateLocation
par:
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyThreeKilometers];
[self.locationManager setDistanceFilter:99999];
qui semble éteindre efficacement le GPS. Le sélecteur pour l'arrière-plan NSTimer
devient alors:
- (void) changeAccuracy {
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kCLDistanceFilterNone];
}
Tout ce que je fais, c'est d'inverser périodiquement la précision pour obtenir une coordonnée de haute précision toutes les quelques minutes. Étant donné que locationManager
n'a pas été arrêté, backgroundTimeRemaining
conserve sa valeur maximale. Cela a réduit la consommation de la batterie d'environ 10% par heure (avec une constante kCLLocationAccuracyBest
en arrière-plan) à environ 2% par heure sur mon appareil.
Si vous avez la UIBackgroundModes
dans votre plist avec la clé location
, vous n'avez pas besoin d'utiliser la méthode beginBackgroundTaskWithExpirationHandler
. C'est redondant. De plus, vous l'utilisez incorrectement (voir ici ), mais c'est inutile puisque votre plist est en place.
Avec UIBackgroundModes location
dans la fenêtre d’application, l’application continuera de fonctionner en arrière-plan indéfiniment aussi longtemps que CLLocationManger
est en cours d’exécution. Si vous appelez stopUpdatingLocation
en arrière-plan, l'application s'arrête et ne redémarre plus.
Peut-être pourriez-vous appeler beginBackgroundTaskWithExpirationHandler
juste avant d'appeler stopUpdatingLocation
et ensuite, après avoir appelé startUpdatingLocation
, vous pourriez appeler endBackgroundTask
pour le garder en arrière-plan lorsque le GPS est arrêté, mais je n'ai jamais essayé cela - ce n'est qu'une idée.
Une autre option (que je n'ai pas encore essayée) est de laisser le gestionnaire de localisation actif en arrière-plan, mais une fois que vous avez trouvé un emplacement précis, modifiez la propriété desiredAccuracy
à 1000 m ou plus pour permettre à la puce GPS de s'éteindre (pour économiser la batterie). . Puis, 10 minutes plus tard, lorsque vous avez besoin d'une autre mise à jour d'emplacement, modifiez la valeur desiredAccuracy
à 100m pour allumer le GPS jusqu'à obtenir un emplacement précis. Répétez l'opération.
Lorsque vous appelez startUpdatingLocation
sur le responsable de la localisation, vous devez lui laisser le temps d’obtenir un poste. Vous ne devez pas appeler immédiatement stopUpdatingLocation
. Nous le laissons fonctionner pendant 10 secondes maximum ou jusqu'à ce que nous obtenions un emplacement haute précision non mis en cache.
Vous devez filtrer les emplacements mis en cache et vérifier l'exactitude de l'emplacement que vous obtenez pour vous assurer qu'il correspond au minimum requis (voir ici ). La première mise à jour que vous obtenez peut être vieille de 10 minutes ou 10 minutes. La première précision que vous obtenez peut être 3000m.
Pensez plutôt à utiliser les API de changement d'emplacement significatif. Une fois que vous avez reçu la notification de changement significatif, vous pouvez démarrer CLLocationManager
pendant quelques secondes pour obtenir une position très précise. Je ne suis pas certain, je n'ai jamais utilisé les services importants de changement de lieu.
Lorsqu'il est temps de démarrer le service de localisation et d'arrêter la tâche en arrière-plan, celle-ci doit être arrêtée avec un retard (une seconde devrait suffire). Sinon, le service de localisation ne démarrera pas. De plus, le service de localisation doit rester allumé pendant quelques secondes (par exemple, 3 secondes).
Il existe un cocoapod APScheduledLocationManager qui permet d'obtenir des mises à jour d'emplacement d'arrière-plan toutes les n secondes avec la précision d'emplacement souhaitée.
let manager = APScheduledLocationManager(delegate: self)
manager.startUpdatingLocation(interval: 170, acceptableLocationAccuracy: 100)
Le référentiel contient également un exemple d'application écrit dans Swift 3.
Pourquoi ne pas essayer avec l'API startMonitoringSignificantLocationChanges:
? Il tire définitivement avec moins de fréquence et la précision est raisonnablement bonne. De plus, il présente de nombreux avantages par rapport aux autres API locationManager
.
Beaucoup plus concernant cette API a déjà été discuté à ce sujet link
Vous devez ajouter le mode de mise à jour d'emplacement dans votre application en ajoutant la clé suivante dans votre info.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 cet appel de méthode
Après iOS 8, Apple a apporté plusieurs modifications à l'extraction en arrière-plan et aux mises à jour liées à la structure CoreLocation.
1) Apple introduit la demande AlwaysAuthorization pour extraire la mise à jour de la localisation dans l'application.
2) Apple introduit backgroundLocationupdates pour extraire 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 fonctionnalité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
}
}
Pour utiliser les services de localisation standard lorsque l'application est en arrière-plan, vous devez d'abord activer les modes de fond dans l'onglet Capacités des paramètres de la cible, puis sélectionner Mises à jour de la localisation.
Ou, ajoutez-le directement à la Info.plist .
<key>NSLocationAlwaysUsageDescription</key>
<string>I want to get your location Information in background</string>
<key>UIBackgroundModes</key>
<array><string>location</string> </array>
Ensuite, vous devez configurer le CLLocationManager
Objectif C
//The Location Manager must have a strong reference to it.
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
//Request Always authorization (iOS8+)
if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) { [_locationManager requestAlwaysAuthorization];
}
//Allow location updates in the background (iOS9+)
if ([_locationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) { _locationManager.allowsBackgroundLocationUpdates = YES;
}
[_locationManager startUpdatingLocation];
Rapide
self.locationManager.delegate = self
if #available (iOS 8.0,*) {
self.locationManager.requestAlwaysAuthorization()
}
if #available (iOS 9.0,*) {
self.locationManager.allowsBackgroundLocationUpdates = true
}
self.locationManager.startUpdatingLocation()
Réf.: https://medium.com/@javedmultani16/location-service-in-the-background-ios-942c89cd99ba