40 votes

Android - Meilleure façon d'implémenter un LocationListener dans plusieurs activités

Je suis bloqué sur ce problème depuis un certain temps. Je travaille sur une application qui utilise la localisation de manière assez intensive dans plusieurs activités différentes. Tous les exemples que j'ai trouvés utilisent un LocationListener distinct dans chaque Activity. Ce n'est pas faisable dans ma situation.

Je me demande quel est le moyen le plus efficace de garder la trace de l'emplacement de l'utilisateur à travers plusieurs activités. Pour l'instant, j'ai créé un service qui implémente LocationListener et utilise une diffusion pour mettre à jour les champs statiques lat et long dans une classe de base Activity. Cela signifie que le service est toujours en cours d'exécution, ce qui n'est pas idéal. Mais si j'arrête le service et ne le relance que lorsque j'en ai besoin, il faut du temps pour obtenir une bonne localisation. J'ai besoin des données de localisation dans la onCreate() de l'activité. C'est la même chose si j'essaie de l'implémenter dans la classe de base de l'activité. Si j'enregistre constamment l'écouteur dans onResume/onCreate et le désenregistre dans onPause(), cela prend trop de temps pour commencer à recevoir des mises à jour. J'ai également essayé de créer un service auquel je pourrais me lier, afin qu'il ne démarre que lorsque j'ai besoin d'un emplacement. Mais j'ai le même problème, il faut trop de temps pour se lier au service et commencer à recevoir des mises à jour.

Le service que j'utilise actuellement fonctionne, mais d'après tout ce que j'ai lu, je ne devrais pas utiliser un service fonctionnant en permanence pour des choses aussi triviales. Mais l'objectif de l'application est de fournir des données pertinentes en fonction de l'emplacement actuel de l'utilisateur. J'ai donc un service qui fonctionne en arrière-plan et fournit des mises à jour périodiques. Le seul problème majeur qui m'a poussé à réexaminer la conception à ce stade est que j'ai récemment découvert que onProviderEnabled() n'est pas appelé si l'utilisateur lance l'application sans que le GPS soit activé et qu'il l'active ensuite. Dans ce cas, l'application n'a aucun moyen de reconnaître que le GPS a été activé pour pouvoir commencer à écouter les mises à jour.

Je pensais avoir compris LocationManager et LocationListener en regardant les exemples, mais je n'arrive pas à les appliquer à cette situation où j'ai besoin de données de localisation dans plusieurs activités. Toute aide ou conseil serait grandement apprécié.

43voto

Devunwired Points 27023

La façon dont je mettrais en œuvre cette exigence est d'utiliser une implémentation de service lié, comme celle de l'application Exemple de service local dans la documentation du SDK. Vous connaissez évidemment l'avantage du service qui vous permet de créer tous les codes de localisation une seule fois.

L'accès au service par le biais de liaisons permet au service de démarrer et de s'arrêter de lui-même, de sorte qu'il ne fonctionne pas lorsque votre application n'est pas au premier plan (il meurt dès qu'il n'y a plus d'activités liées). La clé, selon moi, pour que cela fonctionne bien, est de lier le service dans le fichier onStart() et UNBIND dans onStop() car ces deux appels se chevauchent lorsque vous passez d'une activité à une autre (la deuxième activité commence avant que la première ne s'arrête). Ainsi, le service ne meurt pas lorsqu'on se déplace à l'intérieur de l'application, et il ne meurt que lorsque l'application entière (ou du moins toute partie intéressée par la localisation) quitte le premier plan.

Avec les liaisons, il n'est pas nécessaire de transmettre les données de localisation dans une diffusion, car l'activité peut appeler des méthodes directement sur le service pour obtenir la dernière localisation. Cependant, une diffusion serait toujours avantageuse comme méthode pour indiquer QUAND une nouvelle mise à jour est disponible... mais cela deviendrait juste une notification à l'activité qui écoute pour qu'elle appelle la fonction getLocation() sur le service.

Mon avis. J'espère que cela vous aidera !

17voto

Spipau Points 412

J'ai eu le même problème et j'ai essayé de le résoudre avec la bonne réponse de Devunwired, mais j'ai eu quelques problèmes. Je n'ai pas trouvé le moyen d'arrêter le service et lorsque j'ai terminé mon application, le module GPS était toujours en marche. J'ai donc trouvé un autre moyen :

J'ai écrit une classe GPS.java :

public class GPS {

    private IGPSActivity main;

    // Helper for GPS-Position
    private LocationListener mlocListener;
    private LocationManager mlocManager;

    private boolean isRunning;

    public GPS(IGPSActivity main) {
        this.main = main;

        // GPS Position
        mlocManager = (LocationManager) ((Activity) this.main).getSystemService(Context.LOCATION_SERVICE);
        mlocListener = new MyLocationListener();
        mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
        // GPS Position END
        this.isRunning = true;
    }

    public void stopGPS() {
        if(isRunning) {
            mlocManager.removeUpdates(mlocListener);
            this.isRunning = false;
        }
    }

    public void resumeGPS() {
        mlocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mlocListener);
        this.isRunning = true;
    }

    public boolean isRunning() {
        return this.isRunning;
    }

    public class MyLocationListener implements LocationListener {

        private final String TAG = MyLocationListener.class.getSimpleName();

        @Override
        public void onLocationChanged(Location loc) {
            GPS.this.main.locationChanged(loc.getLongitude(), loc.getLatitude());
        }

        @Override
        public void onProviderDisabled(String provider) {
            GPS.this.main.displayGPSSettingsDialog();
        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

    }

}

Cette classe est utilisée dans toutes les activités qui ont besoin des coordonnées GPS. Chaque activité doit implémenter l'interface suivante (nécessaire pour la communication) :

public interface IGPSActivity {
    public void locationChanged(double longitude, double latitude);
    public void displayGPSSettingsDialog();
}

Maintenant, mon activité principale ressemble à ça :

public class MainActivity extends Activity implements IGPSActivity {

    private GPS gps;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        gps = new GPS(this);
    }

    @Override
    protected void onResume() { 
        if(!gps.isRunning()) gps.resumeGPS();   
        super.onResume();
    }

    @Override
    protected void onStop() {
        gps.stopGPS();
        super.onStop();
    }

    public void locationChanged(double longitude, double latitude) {
        Log.d(TAG, "Main-Longitude: " + longitude);
        Log.d(TAG, "Main-Latitude: " + latitude);
    }

    @Override
    public void displayGPSSettingsDialog() {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(intent);
    }
}

et un deuxième comme ça :

public class TEST4GPS extends Activity implements IGPSActivity{

    private GPS gps;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.gps = new GPS(this);
    }

    @Override
    public void locationChanged(double longitude, double latitude) {
        Log.d("TEST", "Test-Longitude: " + longitude);
        Log.d("TEST", "Test-Latitude: " + latitude);

    }

    @Override
    protected void onResume() { 
        if(!gps.isRunning()) gps.resumeGPS();   
        super. onResume();
    }

    @Override
    protected void onStop() {
        gps.stopGPS();
        super.onStop();
    }

    @Override
    public void displayGPSSettingsDialog() {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                startActivity(intent);

    }
}

Ce n'est pas aussi beau que la solution de Devunwired, mais ça marche pour moi. cya

0voto

travega Points 3000

Vous pourriez faire en sorte que votre service écrive vos coordonnées de latitude et de longitude dans une base de données sqlite lors d'un changement. De cette façon, vous n'auriez pas à supporter les frais de liaison du service et vos coordonnées seraient accessibles à travers toutes vos activités.

Dans votre activité principale, vous pouvez peut-être vérifier l'état du GPS et, s'il est désactivé, vous pouvez inviter l'utilisateur à l'activer. Une fois le GPS activé, lancez votre service.

-1voto

lenooh Points 507

Voici un moyen très simple et direct de le faire :

Dans votre activité principale :

private boolean mFinish;

@Override
public void onBackPressed() {
    mFinish = true;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mFinish = false;
    ...
}

@Override
public void onPause(){
    super.onPause();
    if (mFinish) {
        // remove updates here
        mLocationManager.removeUpdates(mLocationListener);
    }
}

Cela ne supprimera l'écouteur de mises à jour que si vous appuyez sur Retour dans votre activité principale, mais sinon il restera actif.

Ce n'est pas la meilleure façon de faire, mais c'est une solution simple de copier-coller.

Veillez à ne pas appeler l'écouteur de localisation s'il est déjà en cours d'exécution (par exemple, lors de la rotation de l'écran), sinon vous vous retrouverez avec plusieurs écouteurs fonctionnant en arrière-plan.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X