2017-09-16 8 views
1

Je viens de commencer à utiliser gluon mobile et je développe une petite application iOS. J'ai réussi à utiliser le PositionService pour mettre à jour la position des utilisateurs sur une étiquette dans l'interface utilisateur. Maintenant, je voudrais obtenir des mises à jour de localisation même si l'application est en mode d'arrière-plan. En raison de la documentation des développeurs Apple cela devrait fonctionner en ajoutant la clé suivante aux applications plistUtilisation des services de localisation d'arrière-plan sur iOS avec gluon mobile

<key>UIBackgroundModes</key> 
<array> 
    <string>location</string> 
</array> 

Lors du déploiement de l'application sur un iPhone, je peux voir les mises à jour tant que l'application est active. Lorsque vous allez à l'écran d'accueil, la mise à jour s'arrête et dans le terminal (gradle -> launchIOSDevice) le message "Arrêter la mise à jour" s'affiche. Une idée de pourquoi je ne reçois pas les mises à jour de localisation en mode d'arrière-plan?

Voici le code correspondant:

Services.get(PositionService.class).ifPresent(service -> { 
     service.positionProperty().addListener((obs, oldPos, newPos) -> { 
      posLbl.setText(String.format(" %.7f %.7f\nLast update: " + LocalDateTime.now().toString(), 
        newPos.getLatitude(), newPos.getLongitude())); 
      handleData(); 
     }); 
    }); 

Voici l'entrée plist pertinente:

<key>NSLocationAlwaysUsageDescription</key> 
<string>A good reason.</string> 
<key>UIBackgroundModes</key> 
<array> 
    <string>location</string> 
</array> 
<key>NSLocationWhenInUseUsageDescription</key> 
<string>An even better reason.</string> 

Répondre

0

La raison pour laquelle le service de position ne fonctionne pas lorsque l'application passe en mode arrière-plan se trouve here :

Services.get(LifecycleService.class).ifPresent(l -> { 
     l.addListener(LifecycleEvent.PAUSE, IOSPositionService::stopObserver); 
     l.addListener(LifecycleEvent.RESUME, IOSPositionService::startObserver); 
    }); 
startObserver(); 

Le service de cycle de vie est conçu pour empêcher choses inutiles lorsque l'application est en arrière-plan principalement pour économiser la batterie. De nombreux services, dont Position ou Batterie, l'utilisent par défaut. Jusqu'à présent, il n'y a pas de moyen facile de supprimer l'écouteur, car il n'y a pas d'API pour activer ou désactiver son utilisation. Si vous pensez que cela devrait être ajouté, vous pouvez déposer un problème here.

Vous pouvez épingler le référentiel Charm Down, supprimer le code associé, et le recréer à l'aide de votre propre capture instantanée, mais bien sûr ce n'est pas une bonne solution à long terme. Pour l'instant, la seule façon de penser, sans modifier Down, est d'éviter l'inclusion de l'implémentation du service Lifecycle pour iOS. Ainsi, une fois que vous aurez ouvert l'application et instancié le service Position, startObserver sera appelé et ne sera jamais arrêté (jusqu'à la fermeture de l'application).

Dans votre fichier build.gradle, au lieu d'utiliser le bloc downConfig pour inclure le plugin position, faites-le dans le bloc dependencies, et supprimer la dépendance traversal au cycle de vie ios:

dependencies { 
    compile 'com.gluonhq:charm:4.3.7' 
    compile 'com.gluonhq:charm-down-plugin-position:3.6.0' 
    iosRuntime('com.gluonhq:charm-down-plugin-position-ios:3.6.0') { 
     exclude group: 'com.gluonhq', module: 'charm-down-plugin-lifecycle-ios' 
    } 
} 

jfxmobile { 
    downConfig { 
     version = '3.6.0' 
     plugins 'display', 'statusbar', 'storage' 
    } 

déployer maintenant à votre iOS et vérifiez si le service de positionnement fonctionne en mode d'arrière-plan.

EDIT

Comme indiqué, supprimant l'écouteur du cycle de vie qui a arrêté l'observateur ne suffit pas: l'emplacement est pas mis à jour en arrière-plan. La solution pour que cela fonctionne nécessite de modifier le service Position pour iOS et de créer un instantané local.

Ce sont les étapes (pour Mac uniquement):

1.Clone/fork Charm Down

Charm Down est une bibliothèque open source que l'on peut trouver here.

2. Modifiez le service Position pour iOS

Nous devons commenter ou supprimer les auditeurs du cycle de vie de IOSPositionService (link):

public IOSPositionService() { 
    position = new ReadOnlyObjectWrapper<>(); 

    startObserver(); 
} 

(mais une meilleure approche ajoutera API pour autoriser le mode d'arrière-plan, et installer les écouteurs basés sur lui.)

Et maintenant nous devons modifier la na la mise en œuvre tive à Position.m (link):

- (void)startObserver 
{ 
    ... 
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) 
    { 
     // try to save battery by using GPS only when app is used: 
     [self.locationManager requestWhenInUseAuthorization]; 
    } 

    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) 
    { 
     // allow background mode 
     self.locationManager.allowsBackgroundLocationUpdates = YES; 
    } 
    NSLog(@"Start updating location"); 
    ... 
} 

(encore une fois, cela devrait être réglée sur la base d'une API en arrière-plan)

3. Construire et installer

A la racine de charme vers le bas et en utilisant un Mac, exécutez ceci:

./gradlew clean install 

(si le sdk Android n'est pas installé, l'andr Les services OID peuvent être commentés à settings.gradle).

Cela installera un instantané des services de réduction de charme (actuellement 3.7.0-SNAPSHOT).

4. Mise à jour le gluon projet Mobile

Modifier le fichier build.gradle, et définir le référentiel mavenLocal(), et la version instantanée:

Enregistrer et recharger le projet.

5. Utiliser le service de position en arrière-plan

Comme indiqué dans les commentaires, lors de l'exécution sur le mode d'arrière-plan, iOS ne permet pas de faire des changements dans l'interface utilisateur. Cela signifie que chaque fois qu'une nouvelle position est extraite du service, nous devons utiliser un tampon pour le stocker et seulement lorsque l'utilisateur reprend l'application, nous ferons la mise à jour nécessaire à l'interface utilisateur avec tous ces tampons Emplacements. C'est un cas d'utilisation simple: avec le service Lifecycle, nous savons si nous sommes en premier plan ou en arrière-plan, et nous ne mettons à jour que le contrôle ListView lorsque l'application s'exécute en premier plan ou simplement en arrière-plan.

private final BooleanProperty foreground = new SimpleBooleanProperty(true); 

private final Map<String, String> map = new LinkedHashMap<>(); 
private final ObservableList<String> positions = FXCollections.observableArrayList(); 

public BasicView(String name) { 
    super(name); 

    Services.get(LifecycleService.class).ifPresent(l -> { 
     l.addListener(LifecycleEvent.PAUSE,() -> foreground.set(false)); 
     l.addListener(LifecycleEvent.RESUME,() -> foreground.set(true)); 
    }); 
    ListView<String> listView = new ListView<>(positions); 

    Button button = new Button("Start GPS"); 
    button.setGraphic(new Icon(MaterialDesignIcon.GPS_FIXED)); 
    button.setOnAction(e -> { 
     Services.get(PositionService.class).ifPresent(service -> { 
      foreground.addListener((obs, ov, nv) -> { 
       if (nv) { 
        positions.addAll(map.values()); 
       } 
      }); 
      service.positionProperty().addListener((obs, oldPos, newPos) -> { 
       if (foreground.get()) { 
        positions.add(addPosition(newPos)); 
       } else { 
        map.put(LocalDateTime.now().toString(), addPosition(newPos)); 
       } 
      }); 
     }); 
    }); 

    VBox controls = new VBox(15.0, button, listView); 
    controls.setAlignment(Pos.CENTER); 

    setCenter(controls); 
} 

private String addPosition(Position position) { 
    return LocalDateTime.now().toString() + " :: " + 
      String.format("%.7f, %.7f", position.getLatitude(), position.getLongitude()) + 
      " :: " + (foreground.get() ? "F" : "B"); 
} 

Enfin, comme l'a souligné l'OP, assurez-vous d'ajouter les clés nécessaires à la plist:

<key>NSLocationAlwaysUsageDescription</key> 
<string>A good reason.</string> 
<key>UIBackgroundModes</key> 
<array> 
    <string>location</string> 
</array> 
<key>NSLocationWhenInUseUsageDescription</key> 
<string>An even better reason.</string> 

6.Déployer et exécuter

Autoriser l'utilisation de l'emplacement, démarrer le service de localisation et, en mode d'arrière-plan, une barre d'état bleue sur l'appareil iOS indique que l'application utilise l'emplacement.

Notez que cela pourrait vider la batterie très rapidement.

+0

Merci pour votre réponse. J'ai essayé votre deuxième solution et en effet, je ne reçois plus le message "Arrêter la mise à jour" dans la ligne de commande. Malheureusement, je ne reçois pas les mises à jour de localisation en arrière-plan. J'ai manuellement activé l'option «Toujours» pour l'application (Paramètres-> Confidentialité-> Services de localisation-> Toujours). Avez-vous d'autres suggestions? – deviax

+0

Je viens de vérifier le service, et en effet, il y a quelque chose qui manque dans l'implémentation native. Depuis iOS 9.0, il est nécessaire de le faire fonctionner en arrière-plan: 'self.locationManager.allowsBackgroundLocationUpdates = YES;'. Si vous pouvez cloner Charm Down, ajoutez cette ligne à 'startObserver()', sauvegardez, lancez './gradlew clean install', et vous pourrez utiliser la version 3.7.0-SNAPSHOT (ajoutez' mavenLocal() 'à les dépôts). –

+0

okay, j'ai essayé de construire le charme cloué vers le bas mais j'ai obtenu une erreur que ANDROID_HOME n'est pas défini. '* Qu'est-ce qui n'a pas fonctionné: Un problème est survenu lors de l'évaluation du projet racine 'Charm Down'. > Impossible d'obtenir la propriété inconnue 'ANDROID_HOME' pour le projet ': core/android' de type org.gradle.api.Project.' – deviax