Come posso controllare lo stato corrente del ricevitore GPS?


90

Come posso controllare lo stato corrente del ricevitore GPS? Ho già controllato il LocationListener onStatusChangedmetodo ma in qualche modo sembra che non funzioni, o solo la possibilità sbagliata.

Fondamentalmente ho solo bisogno di sapere se l'icona GPS nella parte superiore dello schermo lampeggia (nessuna correzione effettiva) o è fissa (la correzione è disponibile).

Risposte:


150

Come sviluppatore di SpeedView: tachimetro GPS per Android, devo aver provato ogni possibile soluzione a questo problema, tutte con lo stesso risultato negativo. Ribadiamo cosa non funziona:

  1. onStatusChanged () non viene chiamato su Eclair e Froyo.
  2. Contare semplicemente tutti i satelliti disponibili è, ovviamente, inutile.
  3. Anche controllare se uno qualsiasi dei satelliti restituisce true per usedInFix () non è molto utile. Il sistema perde chiaramente la correzione ma continua a segnalare che ci sono ancora diversi sat che vengono utilizzati al suo interno.

Quindi ecco l'unica soluzione funzionante che ho trovato e quella che effettivamente uso nella mia app. Supponiamo di avere questa semplice classe che implementa GpsStatus.Listener:

private class MyGPSListener implements GpsStatus.Listener {
    public void onGpsStatusChanged(int event) {
        switch (event) {
            case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                if (mLastLocation != null)
                    isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

                if (isGPSFix) { // A fix has been acquired.
                    // Do something.
                } else { // The fix has been lost.
                    // Do something.
                }

                break;
            case GpsStatus.GPS_EVENT_FIRST_FIX:
                // Do something.
                isGPSFix = true;

                break;
        }
    }
}

OK, ora in onLocationChanged () aggiungiamo quanto segue:

@Override
public void onLocationChanged(Location location) {
    if (location == null) return;

    mLastLocationMillis = SystemClock.elapsedRealtime();

    // Do something.

    mLastLocation = location;
}

E questo è tutto. Fondamentalmente, questa è la linea che fa tutto:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

Ovviamente puoi modificare il valore in millisecondi, ma ti suggerisco di impostarlo intorno a 3-5 secondi.

Funziona effettivamente e sebbene non abbia esaminato il codice sorgente che disegna l'icona GPS nativa, questo si avvicina a replicare il suo comportamento. Spero che questo aiuti qualcuno.


Ciao, mi chiedo perché contare i satelliti disponibili è inutile come in questo esempio? Se il numero di satelliti trovati è 0 significa nessuna connessione, o mi sbaglio? groups.google.com/group/android-developers/browse_thread/thread/…
per_jansson

3
Ciao Stephen, puoi spiegare perché isGPSFix funziona. Grazie, Nick
nickfox

A proposito, ho notato che in ICS hanno aumentato il tempo fino a quando il sistema inizia a segnalare che il fix GPS è stato perso. Almeno nella 4.0.3 sono esattamente 10 secondi.
soundmaven

Funziona alla grande e anche l'uso di 10 secondi anziché 3-5 per ICS funziona abbastanza bene. Fortunatamente la correzione non è una funzionalità necessaria della mia app, ma è bene essere in grado di conoscerla. Grazie.
Tom

1
Ma questa interruzione non è mLastLocationMillisstata impostata da una sorgente diversa dal GPS? Il sistema affermerebbe che isGPSFixè vero ma in realtà è una soluzione qualsiasi (forse una dalla rete telefonica)
Patrick

25

L'icona GPS sembra cambiare il suo stato in base agli intenti della trasmissione ricevuta. Puoi modificarne lo stato tu stesso con i seguenti esempi di codice:

Notifica che il GPS è stato abilitato:

Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", true);
sendBroadcast(intent);

Avvisa che il GPS sta ricevendo correzioni:

Intent intent = new Intent("android.location.GPS_FIX_CHANGE");
intent.putExtra("enabled", true);
sendBroadcast(intent);

Avvisa che il GPS non riceve più correzioni:

Intent intent = new Intent("android.location.GPS_FIX_CHANGE");
intent.putExtra("enabled", false);
sendBroadcast(intent);

Avvisa che il GPS è stato disabilitato:

Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", false);
sendBroadcast(intent);

Codice di esempio per registrare il ricevitore agli intenti:

// MyReceiver must extend BroadcastReceiver
MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter("android.location.GPS_ENABLED_CHANGE");
filter.addAction("android.location.GPS_FIX_CHANGE");
registerReceiver(receiver, filter);

Ricevendo questi intenti di trasmissione è possibile notare i cambiamenti nello stato del GPS. Tuttavia, sarai avvisato solo quando lo stato cambia. Pertanto non è possibile determinare lo stato corrente utilizzando questi intenti.


Non so come puoi inviare quella trasmissione dalla tua applicazione perché sembra che sia una trasmissione protetta dal sistema, il che significa che solo il sistema può inviarla. Qualsiasi app che invia tale intento dovrebbe bloccarsi.
JoxTraex

È stato possibile inviarlo sei anni fa, quando ho scritto la risposta. Apparentemente da allora hanno aggiunto una certa protezione al sistema.
sast

Stavo usando questi intenti per capire quando altre app stavano usando il GPS. Credo che a partire da Nougat non si possa nemmeno più ascoltare questi intenti, o quello o sono cambiati! Non voglio ascoltare la posizione con la mia app. Qualcuno ha altre idee?
Flyview

Sì, ho quel crash: 05-14 10: 25: 24.113 17344-17344 / xxx.yyy.zzz.debug E / AndroidRuntime: ECCEZIONE FATALE: Processo principale: xxx.yyy.zzz.debug, PID: 17344 java. lang.SecurityException: autorizzazione negata: non è consentito inviare la trasmissione android.location.GPS_FIX_CHANGE da pid = 17344, uid = 10416
unlimited101

19

nuovo membro quindi sfortunatamente non sono in grado di commentare o votare, tuttavia il post di Stephen Daye sopra è stata la soluzione perfetta per lo stesso identico problema per cui stavo cercando aiuto.

una piccola modifica alla riga seguente:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < 3000;

per:

isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < (GPS_UPDATE_INTERVAL * 2);

fondamentalmente mentre sto costruendo un gioco a ritmo lento e il mio intervallo di aggiornamento è già impostato su 5 secondi, una volta che il segnale GPS è uscito per 10+ secondi, questo è il momento giusto per attivare qualcosa.

ciao amico, ho trascorso circa 10 ore cercando di risolvere questa soluzione prima di trovare il tuo post :)


14

Ok, quindi proviamo una combinazione di tutte le risposte e gli aggiornamenti finora e facciamo qualcosa di simile:

L'ascoltatore GPS potrebbe essere qualcosa del genere:

GpsStatus.Listener listener = new GpsStatus.Listener() {
    void onGpsStatusChanged(int event) {
        if (event == GPS_EVENT_SATELLITE_STATUS) {
            GpsStatus status = mLocManager.getGpsStatus(null);
            Iterable<GpsSatellite> sats = status.getSatellites();
            // Check number of satellites in list to determine fix state
        }
    }
}

Le API non sono un po 'chiare su quando e quali informazioni GPS e satellitari vengono fornite, ma penso che un'idea sarebbe quella di guardare quanti satelliti sono disponibili. Se è inferiore a tre, non puoi avere una soluzione. Se è di più, dovresti avere una soluzione.

Prova ed errore sono probabilmente la strada da percorrere per determinare la frequenza con cui Android riporta le informazioni sui satelliti e quali informazioni GpsSatellitecontiene ogni oggetto.


il problema con il conteggio dei satelliti disponibili è che anche se hai 5 satelliti in vista, ciò non significa che sia sempre possibile un fix. (Hai menzionato la correttezza scrivendo "Se è di più, dovresti avere una soluzione")
nr1

Infatti. Anche se non so più quali informazioni sono necessarie per costituire una correzione, o come / se questa può essere recuperata da un GpsSatelliteoggetto.
Christopher Orr

Un altro pensiero .. non lo dici nella tua domanda, ma hai provato a usare solo LocationManager.requestLocationUpdatescon le impostazioni di tempo e distanza impostate su 0? Questo dovrebbe inviarti correzioni GPS non appena si verificano. Se non ricevi nulla, molto probabilmente non hai una soluzione. Puoi combinarlo con l'ascoltatore di stato sopra, se lo desideri.
Christopher Orr

1
Forse ripetendo "sats" e controllando usedInFix () in GpsSatellite?
DeliriumTremens

8

Dopo alcuni anni di lavoro con il GPS su Windows Mobile, ho capito che il concetto di "perdere" un fix GPS può essere soggettivo. Per ascoltare semplicemente ciò che ti dice il GPS, l'aggiunta di un NMEAListener e l'analisi della frase ti diranno se la correzione era "valida" o meno. Vedi http://www.gpsinformation.org/dale/nmea.htm#GGA . Sfortunatamente con alcuni GPS questo valore oscillerà avanti e indietro anche durante il normale corso delle operazioni in un'area di "buon fix".

Quindi, l'altra soluzione è confrontare l'ora UTC della posizione GPS con l'ora del telefono (convertita in UTC). Se sono a una certa differenza di fuso orario, puoi presumere di aver perso la posizione GPS.


Vorresti spiegare il metodo di confronto del tempo? Nella risposta di Stephen Daye, ha confrontato le differenze di orario tra il momento in cui è avvenuta l'ultima correzione e l'ultimo evento GPS_EVENT_SATELLITE_STATUS ... Non sono sicuro di cosa stia misurando.
Rokey Ge

7

mi trovo in un problema simile mentre lavoravo al mio progetto di Master, sembra che la risposta di Daye abbia erroneamente segnalato "nessuna correzione" mentre il dispositivo rimane in una posizione statica. Ho modificato leggermente la soluzione che sembra funzionare bene per me in una posizione statica. Non so come influirebbe sulla batteria in quanto non è la mia preoccupazione principale, ma ecco come l'ho fatto richiedendo nuovamente gli aggiornamenti della posizione quando una correzione è scaduta.

private class MyGPSListener implements GpsStatus.Listener {
    public void onGpsStatusChanged(int event) {
        switch (event) {
        case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
            if (Global.getInstance().currentGPSLocation != null)
            {
                if((SystemClock.elapsedRealtime() - mLastLocationMillis) < 20000)
                {
                    if (!hasGPSFix) 
                        Log.i("GPS","Fix Acquired");
                    hasGPSFix = true;
                } 
                else
                {
                    if (hasGPSFix) 
                    {
                        Log.i("GPS","Fix Lost (expired)");
                        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 10, locationListener);
                    }
                    hasGPSFix = false;
                }
            }
            break;
        case GpsStatus.GPS_EVENT_FIRST_FIX:
            Log.i("GPS", "First Fix/ Refix");
            hasGPSFix = true;
            break;
        case GpsStatus.GPS_EVENT_STARTED:
            Log.i("GPS", "Started!");
            break;
        case GpsStatus.GPS_EVENT_STOPPED:
            Log.i("GPS", "Stopped");
            break;
        }
    }
}

5

Bene, mettere insieme ogni approccio lavorativo si tradurrà in questo (anche trattare con deprecati GpsStatus.Listener):

private GnssStatus.Callback mGnssStatusCallback;
@Deprecated private GpsStatus.Listener mStatusListener;
private LocationManager mLocationManager;

@Override
public void onCreate() {
    mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

    mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    if (checkPermission()) {
       mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, GPS_UPDATE_INTERVAL, MIN_DISTANCE, this);
    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        mGnssStatusCallback = new GnssStatus.Callback() {
            @Override
            public void onSatelliteStatusChanged(GnssStatus status) {
                satelliteStatusChanged();
            }

            @Override
            public void onFirstFix(int ttffMillis) {
                gpsFixAcquired();

            }
        };
        mLocationManager.registerGnssStatusCallback(mGnssStatusCallback);
    } else {
        mStatusListener = new GpsStatus.Listener() {
            @Override
            public void onGpsStatusChanged(int event) {
                switch (event) {
                    case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                        satelliteStatusChanged();
                        break;
                    case GpsStatus.GPS_EVENT_FIRST_FIX:
                        // Do something.
                        gpsFixAcquired();
                        break;
                }
            }
        };
        mLocationManager.addGpsStatusListener(mStatusListener);
    }
}

private void gpsFixAcquired() {
    // Do something.
    isGPSFix = true;
}

private void satelliteStatusChanged() {
    if (mLastLocation != null)
        isGPSFix = (SystemClock.elapsedRealtime() - mLastLocationMillis) < (GPS_UPDATE_INTERVAL * 2);

    if (isGPSFix) { // A fix has been acquired.
        // Do something.
    } else { // The fix has been lost.
        // Do something.
    }
}

@Override
public void onLocationChanged(Location location) {
    if (location == null) return;

    mLastLocationMillis = SystemClock.elapsedRealtime();

    mLastLocation = location;
}

@Override
public void onStatusChanged(String s, int i, Bundle bundle) {

}

@Override
public void onProviderEnabled(String s) {

}

@Override
public void onProviderDisabled(String s) {

}

Nota: questa risposta è una combinazione delle risposte precedenti.


1
Finché accrediti altri autori, non ci sono problemi a ottenere i tuoi voti per lavori nuovi o derivati. Le regole qui sono più preoccupate per le persone che plagiano le risposte di altre persone e le usano altrove su Stack Overflow per attirare i rappresentanti in modo disonesto. Dato che hai menzionato che hai costruito su altre risposte, penso che vada bene.
halfer

3

Se hai solo bisogno di sapere se c'è una correzione, controlla l'ultima posizione nota fornita dal ricevitore GPS e controlla il valore .getTime () per sapere quanti anni ha. Se è abbastanza recente (tipo ... pochi secondi) hai una soluzione.

   LocationManager lm = (LocationManager)context.getSystemService(LOCATION_SERVICE); 
   Location loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);

   // Get the time of the last fix
   long lastFixTimeMillis = loc.getTime(); 

... e infine confrontalo con la data e l'ora corrente (in UTC!). Se è abbastanza recente, hai una soluzione.

Lo faccio nella mia app e finora tutto bene.


+1 sembra che valga la pena provare se vuoi sapere immediatamente se c'è una soluzione.
AgentKnopf


2

Potrei sbagliarmi, ma sembra che le persone stiano andando molto fuori tema

ho solo bisogno di sapere se l'icona GPS nella parte superiore dello schermo lampeggia (nessuna correzione effettiva)

Questo è facilmente realizzabile

LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean gps_on = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);

Per vedere se hai una soluzione solida, le cose diventano un po 'più complicate:

public class whatever extends Activity {
    LocationManager lm;
    Location loc;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);        
        lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        loc = null;
        request_updates();        
    }

    private void request_updates() {
        if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
            // GPS is enabled on device so lets add a loopback for this locationmanager
            lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,0, 0, locationListener);
        }      
    }

    LocationListener locationListener = new LocationListener() {
        public void onLocationChanged(Location location) {
            // Each time the location is changed we assign loc
            loc = location;
        }

         // Need these even if they do nothing. Can't remember why.
         public void onProviderDisabled(String arg0) {}
         public void onProviderEnabled(String provider) {}
         public void onStatusChanged(String provider, int status, Bundle extras) {}
    };

Ora ogni volta che vuoi vedere se hai aggiustato?

if (loc != null){
    // Our location has changed at least once
    blah.....
}

Se vuoi essere fantasioso puoi sempre avere un timeout usando System.currentTimeMillis () e loc.getTime ()

Funziona in modo affidabile, almeno su un N1 dal 2.1.


3
La tua soluzione è troppo complessa per quello che fa. Tuttavia fa molto poco. Cosa succede se dopo che il GPS ha risolto la mia posizione, vado alla stazione della metropolitana, facendo perdere al GPS la correzione?
meandre

1

Con LocationManager puoi getLastKnownLocation () dopo aver ottenutoBestProvider (). Questo ti dà un oggetto Location, che ha i metodi getAccuracy () in metri e getTime () in millisecondi UTC

Questo ti dà abbastanza informazioni?

O forse potresti iterare sui LocationProvider e scoprire se ognuno soddisfa i Criteri (ACCURACY_COARSE)


1

tanti post ...

GpsStatus.Listener gpsListener = new GpsStatus.Listener() {
                        public void onGpsStatusChanged(int event) {
                            if( event == GpsStatus.GPS_EVENT_FIRST_FIX){
                                showMessageDialog("GPS fixed");
                            }
                        }
                 };

aggiungendo questo codice, con addGpsListener ... showMessageDialog ... mostra solo una finestra di dialogo standard con la stringa

ha fatto il lavoro perfettamente per me :) grazie mille: =) (sry per questo post, non ancora in grado di votare)


Questa è solo la prima soluzione. E se entri in un tunnel?
Leos Literak

1

Se non hai bisogno di un aggiornamento nell'istante stesso in cui la correzione viene persa, puoi modificare la soluzione di Stephen Daye in questo modo, in modo che tu abbia un metodo che controlla se la correzione è ancora presente.

Quindi puoi controllarlo ogni volta che hai bisogno di alcuni dati GPS e non hai bisogno di GpsStatus.Listener.

Le variabili "globali" sono:

private Location lastKnownLocation;
private long lastKnownLocationTimeMillis = 0;
private boolean isGpsFix = false;

Questo è il metodo chiamato in "onLocationChanged ()" per ricordare l'ora dell'aggiornamento e la posizione corrente. Oltre a ciò aggiorna "isGpsFix":

private void handlePositionResults(Location location) {
        if(location == null) return;

        lastKnownLocation = location;
        lastKnownLocationTimeMillis = SystemClock.elapsedRealtime();

        checkGpsFix(); // optional
    }

Questo metodo viene chiamato ogni volta che ho bisogno di sapere se c'è un fix GPS:

private boolean checkGpsFix(){

    if (SystemClock.elapsedRealtime() - lastKnownLocationTimeMillis < 3000) {
        isGpsFix = true;

    } else {
        isGpsFix = false;
        lastKnownLocation = null;
    }
    return isGpsFix;
}

Nella mia implementazione eseguo prima checkGpsFix () e se il risultato è vero utilizzo la variabile "lastKnownLocation" come posizione corrente.


1

So che è un po 'tardi. Tuttavia, perché non utilizzare NMEAListener se vuoi sapere se hai una soluzione. Da quello che ho letto, il NMEAListener ti darà le frasi NMEA e da lì scegli la frase corretta.

La frase RMC contiene lo stato della correzione che è A per OK o V per avviso. La frase GGA contiene la qualità del fix (0 non valido, 1 GPS o 2 DGPS)

Non posso offrirti alcun codice java perché ho appena iniziato con Android, ma ho creato una libreria GPS in C # per le app Windows, che sto cercando di utilizzare con Xamarin. Mi sono imbattuto in questo thread solo perché stavo cercando informazioni sul provider.

Da quello che ho letto finora sull'oggetto Location non sono così a mio agio con metodi come getAccuracy () e hasAccuracy (). Sono abituato a estrarre dalle frasi NMEA i valori HDOP e VDOP per determinare quanto siano accurate le mie correzioni. È abbastanza comune avere una correzione, ma avere un HDOP scadente, il che significa che la tua precisione orizzontale non è affatto buona. Ad esempio, seduto alla tua scrivania mentre esegui il debug con un dispositivo GPS Bluetooth esterno contro una finestra, è molto probabile che tu ottenga una correzione, ma HDOP e VDOP molto scadenti. Posiziona il tuo dispositivo GPS in un vaso di fiori all'aperto o qualcosa di simile o aggiungi un'antenna esterna al GPS e ottieni immediatamente buoni valori HDOP e VDOP.


0

Forse è la migliore possibilità per creare un TimerTask che imposta regolarmente la posizione ricevuta su un determinato valore (null?). Se un nuovo valore viene ricevuto da GPSListener, aggiornerà la posizione con i dati correnti.

Penso che sarebbe una soluzione funzionante.


0

Dici che hai già provato onStatusChanged (), ma per me funziona.

Ecco il metodo che uso (ho lasciato che la classe stessa gestisse onStatusChanged):

private void startLocationTracking() {
    final int updateTime = 2000; // ms
    final int updateDistance = 10; // meter
    final Criteria criteria = new Criteria();
    criteria.setCostAllowed(false);
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    final String p = locationManager.getBestProvider(criteria, true);
    locationManager.requestLocationUpdates(p, updateTime, updateDistance,
            this);
}

E gestisco onStatusChanged come segue:

void onStatusChanged(final String provider, final int status,
        final Bundle extras) {
    switch (status) {
    case LocationProvider.OUT_OF_SERVICE:
        if (location == null || location.getProvider().equals(provider)) {
            statusString = "No Service";
            location = null;
        }
        break;
    case LocationProvider.TEMPORARILY_UNAVAILABLE:
        if (location == null || location.getProvider().equals(provider)) {
            statusString = "no fix";
        }
        break;
    case LocationProvider.AVAILABLE:
        statusString = "fix";
        break;
    }
}

Si noti che i metodi onProvider {Dis, En} abled () riguardano l'abilitazione e la disabilitazione del tracciamento GPS da parte dell'utente; non è quello che stai cercando.


Sfortunatamente, spesso non funziona. onLocationChanged () viene chiamato una volta al secondo, ma onStatusChanged non viene chiamato affatto. A volte vorrei poter semplicemente interrogare lo stato corrente invece di aspettare una notifica che potrebbe non arrivare per molto tempo, o mai.
Edward Falk

2
Destra. Ho imparato alcune cose da solo dopo quella risposta precedente, e una è che non tutte le piattaforme Android sono fatte allo stesso modo. Vedo sicuramente chiamate a onStatusChanged sia sul mio HTC Hero che sul mio Archos 5, ma non sono sorpreso che non funzioni ovunque. Che ne dici del piano B: usi GPSStatusListener mostrato in un'altra risposta e vedi semplicemente se uno dei satelliti restituisce true per usedInFix (). Nella mia esperienza questo si avvicina di più al comportamento dell'icona GPS standard. (Hai provato a trovare la fonte che implementa
quell'icona

0

L'impostazione dell'intervallo di tempo per verificare la correzione non è una buona scelta .. ho notato che onLocationChanged non viene chiamato se non ti stai muovendo .. cosa è comprensibile poiché la posizione non cambia :)

Il modo migliore sarebbe ad esempio:

  • controllare l'intervallo dall'ultima posizione ricevuta (in gpsStatusChanged)
  • se quell'intervallo è maggiore di 15s imposta la variabile: long_interval = true
  • rimuovi il listener di posizione e aggiungilo di nuovo, di solito ottieni la posizione aggiornata se la posizione è davvero disponibile, in caso contrario - probabilmente hai perso la posizione
  • in onLocationChanged hai appena impostato long_interval su false ..
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.