¿Cómo puedo comprobar el estado actual del receptor GPS?

90

¿Cómo puedo comprobar el estado actual del receptor GPS? Ya verifiqué el LocationListener onStatusChangedmétodo pero de alguna manera parece que no está funcionando, o simplemente es una posibilidad incorrecta.

Básicamente, solo necesito saber si el icono de GPS en la parte superior de la pantalla está parpadeando (no hay una solución real) o fijo (la solución está disponible).

nr1
fuente

Respuestas:

150

Como desarrollador de SpeedView: velocímetro GPS para Android, debo haber probado todas las soluciones posibles a este problema, todas con el mismo resultado negativo. Reiteremos lo que no funciona:

  1. onStatusChanged () no se llama en Eclair y Froyo.
  2. Contar simplemente todos los satélites disponibles es, por supuesto, inútil.
  3. Verificar si alguno de los satélites devuelve verdadero para usedInFix () tampoco es muy útil. El sistema claramente pierde la solución, pero sigue informando que todavía hay varios sats que se utilizan en él.

Así que esta es la única solución funcional que encontré y la que realmente uso en mi aplicación. Digamos que tenemos esta clase simple que 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;
        }
    }
}

Bien, ahora en onLocationChanged () agregamos lo siguiente:

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

    mLastLocationMillis = SystemClock.elapsedRealtime();

    // Do something.

    mLastLocation = location;
}

Y eso es. Básicamente, esta es la línea que lo hace todo:

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

Puede ajustar el valor de milisegundos, por supuesto, pero le sugiero que lo configure alrededor de 3-5 segundos.

Esto realmente funciona y aunque no he mirado el código fuente que dibuja el ícono del GPS nativo, esto se acerca a replicar su comportamiento. Espero que esto ayude a alguien.

soundmaven
fuente
Hola, me pregunto por qué contar los satélites disponibles es inútil como en este ejemplo. Si el número de satélites encontrados es 0 significa que no hay conexión, ¿o me equivoco? groups.google.com/group/android-developers/browse_thread/thread/…
per_jansson
3
Hola Stephen, ¿podrías explicar por qué funciona isGPSFix? Gracias, Nick
nickfox
Por cierto, he notado que en ICS aumentaron el tiempo hasta que el sistema comienza a informar que se ha perdido la posición del GPS. Al menos en 4.0.3 son exactamente 10 segundos.
soundmaven
Funciona muy bien, y usar 10 segundos en lugar de 3-5 para ICS también funciona bastante bien. Afortunadamente, corregir no es una característica necesaria de mi aplicación, pero es bueno poder saberlo. Gracias.
Tom
1
¿Pero no se rompe esto cuando mLastLocationMillisfue configurado por una fuente distinta al GPS? El sistema afirmaría que eso isGPSFixes cierto, pero en realidad es cualquier solución (tal vez una de la red telefónica)
avalancha
25

El icono de GPS parece cambiar su estado según los intentos de transmisión recibidos. Puede cambiar su estado usted mismo con los siguientes ejemplos de código:

Notifique que el GPS se ha habilitado:

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

Notifique que el GPS está recibiendo arreglos:

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

Notifique que el GPS ya no recibe reparaciones:

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

Notifique que el GPS se ha desactivado:

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

Código de ejemplo para registrar el receptor a los intents:

// 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);

Al recibir estos intentos de transmisión, puede notar los cambios en el estado del GPS. Sin embargo, solo se le notificará cuando cambie el estado. Por lo tanto, no es posible determinar el estado actual usando estos intentos.

sast
fuente
No sé cómo puede enviar esa transmisión desde su aplicación porque parece que es una transmisión protegida por el sistema, lo que significa que solo el sistema puede enviarla. Cualquier aplicación que envíe esa intención debería fallar.
JoxTraex
Fue posible enviarlo hace seis años, cuando escribí la respuesta. Aparentemente, han agregado algo de protección al sistema desde entonces.
sábado
Estaba usando estos intentos para saber cuándo otras aplicaciones estaban usando el GPS. Creo que comenzando con Nougat, ya ni siquiera puedes escuchar estas intenciones, ¡o han cambiado! No quiero escuchar la ubicación con mi propia aplicación. ¿Alguien tiene alguna otra idea?
Flyview
Sí, tengo ese bloqueo: 05-14 10: 25: 24.113 17344-17344 / xxx.yyy.zzz.debug E / AndroidRuntime: FATAL EXCEPTION: main Process: xxx.yyy.zzz.debug, PID: 17344 java. lang.SecurityException: Permission Denial: no se permite enviar transmisión android.location.GPS_FIX_CHANGE desde pid = 17344, uid = 10416
ilimitado101
19

nuevo miembro, así que desafortunadamente no puedo comentar o votar, sin embargo, la publicación anterior de Stephen Daye fue la solución perfecta para el mismo problema con el que he estado buscando ayuda.

una pequeña alteración en la siguiente línea:

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

a:

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

Básicamente, como estoy construyendo un juego de ritmo lento y mi intervalo de actualización ya está configurado en 5 segundos, una vez que la señal del GPS se apaga durante más de 10 segundos, ese es el momento adecuado para activar algo.

saludos amigo, pasé aproximadamente 10 horas tratando de resolver esta solución antes de encontrar tu publicación :)

chich
fuente
14

Bien, intentemos una combinación de todas las respuestas y actualizaciones hasta ahora y hagamos algo como esto:

El oyente de GPS podría ser algo como esto:

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
        }
    }
}

Las API no están claras sobre cuándo y qué información de satélite y GPS se proporciona, pero creo que una idea sería ver cuántos satélites hay disponibles. Si está por debajo de tres, entonces no puede tener una solución. Si es más, entonces debería tener una solución.

Probablemente, la prueba y el error sea el camino a seguir para determinar la frecuencia con la que Android reporta información satelital y qué información GpsSatellitecontiene cada objeto.

Christopher Orr
fuente
el problema de contar los satélites disponibles es que incluso si tiene 5 satélites a la vista, esto no significa que siempre sea posible una solución. (Lo mencionó correctamente al escribir "Si es más, entonces debería tener una solución")
nr1
En efecto. Aunque no sé más sobre qué información se requiere para constituir una solución, o cómo / si se puede recuperar de un GpsSatelliteobjeto.
Christopher Orr
Otro pensamiento ... no menciona esto en su pregunta, pero ¿ha intentado usarlo LocationManager.requestLocationUpdatescon la configuración de tiempo y distancia establecida en 0? Eso debería enviarle correcciones de GPS tan pronto como sucedan. Si no recibe nada, lo más probable es que no tenga una solución. Puede combinar esto con el oyente de estado anterior si lo desea.
Christopher Orr
1
¿Iterando "sats" y comprobando usedInFix () en Gps satellite tal vez?
DeliriumTremens
8

Después de algunos años de trabajar con GPS en Windows Mobile, me di cuenta de que el concepto de "perder" una posición de GPS puede ser subjetivo. Para simplemente escuchar lo que le dice el GPS, agregar un NMEAListener y analizar la oración le dirá si la corrección fue "válida" o no. Consulte http://www.gpsinformation.org/dale/nmea.htm#GGA . Desafortunadamente, con algunos GPS, este valor fluctuará hacia adelante y hacia atrás incluso durante el curso normal de funcionamiento en un área de "buena posición".

Entonces, la otra solución es comparar la hora UTC de la ubicación del GPS con la hora del teléfono (convertida a UTC). Si hay una cierta diferencia de tiempo, puede asumir que perdió la posición GPS.

Justin Breitfeller
fuente
¿Le importaría explicar el método de comparación de tiempos? En la respuesta de Stephen Daye, comparó las diferencias de tiempo entre el momento en que ocurrió la última corrección y el último evento GPS_EVENT_SATELLITE_STATUS ... No estoy seguro de lo que está midiendo.
Rokey Ge
7

Tengo un problema similar mientras trabajaba en mi proyecto de maestría, parece que la respuesta de Daye informó erróneamente "no hay solución" mientras el dispositivo permanece en una ubicación estática. Modifiqué la solución un poco, lo que parece funcionar bien para mí en una ubicación estática. No sé cómo afectaría a la batería, ya que no es mi principal preocupación, pero así es como lo hice al volver a solicitar actualizaciones de ubicación cuando se agotó el tiempo de espera.

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;
        }
    }
}
Jeffery Lee
fuente
5

Bueno, reunir todos los enfoques de trabajo dará como resultado esto (también se trata de obsoletos 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: esta respuesta es una combinación de las respuestas anteriores.

Gregory Stein
fuente
1
Siempre que dé crédito a otros autores, no hay problema en obtener sus propios votos positivos para trabajos nuevos o derivados. Las reglas aquí están más preocupadas por que las personas plagien las respuestas de otras personas y las usen en otros lugares de Stack Overflow para atraer representantes de manera deshonesta. Como mencionó que se basó en otras respuestas, creo que está bien.
halfer
3

Si solo necesita saber si hay una solución, verifique la última ubicación conocida proporcionada por el receptor GPS y verifique el valor .getTime () para saber cuántos años tiene. Si es lo suficientemente reciente (como ... unos segundos), tiene una solución.

   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(); 

... y finalmente compárelo con la fecha y hora actual (¡en UTC!). Si es lo suficientemente reciente, tienes una solución.

Lo hago en mi aplicación y hasta ahora todo va bien.

Adán
fuente
Parece que vale la pena probar el +1 si quieres saber inmediatamente si hay una solución.
AgentKnopf
2

Puede que me equivoque, pero parece que la gente se está saliendo del tema

solo necesito saber si el icono de gps en la parte superior de la pantalla está parpadeando (no hay solución real)

Eso se hace fácilmente con

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

Para ver si tiene una solución sólida, las cosas se ponen un poco más complicadas:

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) {}
    };

¿Ahora cuando quieras para ver si tienes solución?

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

Si quieres ser elegante, siempre puedes tener un tiempo de espera usando System.currentTimeMillis () y loc.getTime ()

Funciona de manera confiable, al menos en un N1 desde 2.1.

regomodo
fuente
3
Su solución es demasiado compleja para lo que hace. Sin embargo, hace muy poco. ¿Qué pasa si después de que gps resuelve mi ubicación, voy a la estación de metro, lo que hace que el gps pierda la corrección?
meandre
1

Con LocationManager puede getLastKnownLocation () después de getBestProvider (). Esto le da un objeto Location, que tiene los métodos getAccuracy () en metros y getTime () en milisegundos UTC

¿Esto te da suficiente información?

O tal vez podría iterar sobre LocationProviders y averiguar si cada uno cumple Criterios (ACCURACY_COARSE)

Mark Borgerding
fuente
1

tantas publicaciones ...

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

agregando este código, con addGpsListener ... showMessageDialog ... solo muestra una ventana de diálogo estándar con la cadena

hizo el trabajo perfectamente para mí :) muchas gracias: =) (sry por esta publicación, aún no puedo votar)

cV2
fuente
Esta es solo la primera solución. ¿Y si entras en un túnel?
Leos Literak
1

Si no necesita una actualización en el mismo instante en que se pierde la corrección, puede modificar la solución de Stephen Daye de esa manera, que tenga un método que verifique si la corrección todavía está presente.

Por lo tanto, puede verificarlo siempre que necesite algunos datos de GPS y no necesite ese GpsStatus.Listener.

Las variables "globales" son:

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

Este es el método que se llama dentro de "onLocationChanged ()" para recordar la hora de actualización y la ubicación actual. Además de que actualiza "isGpsFix":

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

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

        checkGpsFix(); // optional
    }

Ese método se llama siempre que necesito saber si hay una corrección de GPS:

private boolean checkGpsFix(){

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

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

En mi implementación, primero ejecuto checkGpsFix () y si el resultado es verdadero, uso la variable "lastKnownLocation" como mi posición actual.

Sebastián Mauthofer
fuente
1

Sé que es un poco tarde. Sin embargo, ¿por qué no utilizar NMEAListener si quiere saber si tiene una solución? Por lo que he leído, NMEAListener te dará las oraciones NMEA y de ahí eliges la oración correcta.

La sentencia RMC contiene el estado de corrección, que es A para OK o V para advertencia. La oración GGA contiene la calidad de corrección (0 inválida, 1 GPS o 2 DGPS)

No puedo ofrecerle ningún código Java ya que recién estoy comenzando con Android, pero he creado una biblioteca GPS en C # para aplicaciones de Windows, que estoy buscando usar con Xamarin. Solo encontré este hilo porque estaba buscando información del proveedor.

Por lo que he leído hasta ahora sobre el objeto Location, no me siento tan cómodo con métodos como getAccuracy () y hasAccuracy (). Estoy acostumbrado a extraer de las sentencias NMEA valores HDOP y VDOP para determinar qué tan precisas son mis correcciones. Es bastante común tener una solución, pero tiene un HDOP pésimo, lo que significa que su precisión horizontal no es muy buena en absoluto. Por ejemplo, sentado en su escritorio depurando con un dispositivo GPS Bluetooth externo contra una ventana, es muy probable que obtenga una solución, pero HDOP y VDOP muy deficientes. Coloque su dispositivo GPS en una maceta en el exterior o algo similar o agregue una antena externa al GPS e inmediatamente obtendrá buenos valores de HDOP y VDOP.

usuario2153142
fuente
0

Tal vez sea la mejor posibilidad crear un TimerTask que establezca la ubicación recibida en un cierto valor (¿nulo?) Regularmente. Si GPSListener recibe un nuevo valor, actualizará la ubicación con los datos actuales.

Creo que sería una solución funcional.

nr1
fuente
0

Dices que ya probaste onStatusChanged (), pero eso funciona para mí.

Este es el método que uso (dejo que la clase maneje el 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);
}

Y manejo el onStatusChanged de la siguiente manera:

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;
    }
}

Tenga en cuenta que los métodos onProvider {Dis, En} abled () tratan de habilitar y deshabilitar el rastreo GPS por parte del usuario; no es lo que estás buscando.

CVR
fuente
Desafortunadamente, a menudo no funciona. onLocationChanged () se llama una vez por segundo, pero onStatusChanged no se llama en absoluto. A veces desearía poder consultar el estado actual en lugar de esperar una notificación que podría no llegar durante mucho tiempo o nunca.
Edward Falk
2
Derecha. Yo mismo aprendí algunas cosas después de esa respuesta anterior, y una es que no todas las plataformas de Android son iguales. Definitivamente veo llamadas a onStatusChanged tanto en mi HTC Hero como en mi Archos 5, pero no me sorprende que no funcione en todas partes. ¿Qué tal el plan B? Usas GPSStatusListener que se muestra en otra respuesta y simplemente ves si alguno de los satélites devuelve verdadero para usedInFix (). En mi experiencia, eso se acerca más al comportamiento del icono de GPS estándar. (¿Ha intentado encontrar la fuente que implementa ese ícono estándar, por cierto?)
CvR
0

Establecer el intervalo de tiempo para verificar la corrección no es una buena opción. Noté que no se llama a onLocationChanged si no se está moviendo ... lo que es comprensible ya que la ubicación no cambia :)

Mejor forma sería por ejemplo:

  • intervalo de verificación hasta la última ubicación recibida (en gpsStatusChanged)
  • si ese intervalo es más de 15 s, establezca la variable: long_interval = true
  • elimine el detector de ubicación y agréguelo nuevamente, por lo general, obtendrá la posición actualizada si la ubicación realmente está disponible, si no, probablemente haya perdido la ubicación
  • en onLocationChanged acaba de establecer long_interval en false ..
Resistente
fuente