Como posso verificar o status atual do receptor GPS?

90

Como posso verificar o status atual do receptor GPS? Já verifiquei o LocationListener onStatusChangedmétodo, mas de alguma forma parece que não está funcionando, ou apenas a possibilidade errada.

Basicamente, só preciso saber se o ícone do GPS na parte superior da tela está piscando (sem correção real) ou sólido (correção disponível).

nr1
fonte

Respostas:

150

Como desenvolvedor do SpeedView: velocímetro GPS para Android, devo ter tentado todas as soluções possíveis para esse problema, todas com o mesmo resultado negativo. Vamos reiterar o que não funciona:

  1. onStatusChanged () não está sendo chamado no Eclair e no Froyo.
  2. Simplesmente contar todos os satélites disponíveis é, obviamente, inútil.
  3. Verificar se algum dos satélites retorna verdadeiro para usedInFix () também não é muito útil. O sistema claramente perde a correção, mas continua relatando que ainda há vários sats usados ​​nele.

Portanto, aqui está a única solução funcional que encontrei e aquela que realmente uso em meu aplicativo. Digamos que temos esta classe simples que implementa o 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, agora em onLocationChanged () adicionamos o seguinte:

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

    mLastLocationMillis = SystemClock.elapsedRealtime();

    // Do something.

    mLastLocation = location;
}

E é isso. Basicamente, esta é a linha que faz tudo:

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

Você pode ajustar o valor em milis, é claro, mas sugiro defini-lo em torno de 3-5 segundos.

Isso realmente funciona e, embora eu não tenha olhado para o código-fonte que desenha o ícone do GPS nativo, ele está perto de replicar seu comportamento. Espero que isso ajude alguém.

soundmaven
fonte
Olá, gostaria de saber por que a contagem dos satélites disponíveis é inútil como neste exemplo? Se o número de satélites encontrados for 0 significa que não há conexão ou estou errado? groups.google.com/group/android-developers/browse_thread/thread/…
per_jansson
3
Olá, Stephen, pode explicar porque funciona o isGPSFix. Obrigado, Nick
nickfox
A propósito, notei que no ICS eles aumentaram o tempo até que o sistema comece a relatar que a posição do GPS foi perdida. Pelo menos no 4.0.3 são precisamente 10 segundos.
soundmaven de
Funciona muito bem e usar 10 segundos em vez de 3-5 para ICS também funciona muito bem. Felizmente, consertar não é um recurso necessário do meu aplicativo, mas é bom saber disso. Obrigado.
Tom
1
Mas isso não quebra quando mLastLocationMillisfoi definido por uma fonte diferente do GPS? O sistema então alegaria que isso isGPSFixé verdade, mas na realidade é qualquer conserto (talvez um da rede telefônica)
Patrick
25

O ícone do GPS parece mudar de estado de acordo com as intenções de transmissão recebidas. Você mesmo pode alterar seu estado com os seguintes exemplos de código:

Notifique que o GPS foi ativado:

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

Notifique que o GPS está recebendo correções:

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

Notifique que o GPS não está mais recebendo correções:

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

Notifique que o GPS foi desativado:

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

Código de exemplo para registrar o receptor nas 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);

Ao receber essas intenções de transmissão, você pode notar as mudanças no status do GPS. No entanto, você será notificado apenas quando o estado mudar. Portanto, não é possível determinar o estado atual usando essas intents.

sast
fonte
Não sei como você consegue enviar essa transmissão de seu aplicativo porque parece que é uma transmissão protegida pelo sistema, o que significa que apenas o sistema pode enviá-la. Qualquer aplicativo que envie essa intenção deve falhar.
JoxTraex
Foi possível enviar há seis anos, quando escrevi a resposta. Aparentemente, eles adicionaram alguma proteção ao sistema desde então.
sast de
Eu estava usando essas intenções para saber quando outros aplicativos estavam usando o GPS. Acredito que começando com o Nougat, você não consegue nem ouvir mais essas intenções, ou elas mudaram! Não quero ouvir a localização com meu próprio aplicativo. Alguém tem outras ideias?
Flyview
Sim, tenho aquele travamento: 05-14 10: 25: 24.113 17344-17344 / xxx.yyy.zzz.debug E / AndroidRuntime: EXCEÇÃO FATAL: Processo principal: xxx.yyy.zzz.debug, PID: 17344 java. lang.SecurityException: Permission Denial: not allowed to send broadcast android.location.GPS_FIX_CHANGE from pid = 17344, uid = 10416
unlimited101
19

novo membro, então infelizmente não posso comentar ou votar, no entanto, a postagem de Stephen Daye acima foi a solução perfeita para exatamente o mesmo problema para o qual estou procurando ajuda.

uma pequena alteração na seguinte linha:

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

para:

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

Basicamente, como estou construindo um jogo de ritmo lento e meu intervalo de atualização já está definido para 5 segundos, uma vez que o sinal gps sai por mais de 10 segundos, esse é o momento certo para acionar algo.

saúde amigo, passei cerca de 10 horas tentando resolver essa solução antes de encontrar sua postagem :)

chich
fonte
14

Ok, então vamos tentar uma combinação de todas as respostas e atualizações até agora e fazer algo assim:

O ouvinte de GPS pode ser algo assim:

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

As APIs não são claras sobre quando e quais informações de GPS e satélite são fornecidas, mas acho que uma ideia seria olhar quantos satélites estão disponíveis. Se estiver abaixo de três, você não pode ter uma correção. Se for mais, então você deve ter uma correção.

Tentativa e erro é provavelmente o caminho a percorrer para determinar a frequência com que o Android relata informações de satélite e quais informações cada GpsSatelliteobjeto contém.

Christopher Orr
fonte
o problema de contar os satélites disponíveis é que mesmo que você tenha 5 satélites à vista, isso não significa que uma correção seja sempre possível. (Você mencionou corretamente escrevendo "Se for mais, então você deve ter uma correção")
nr1
De fato. Embora eu não saiba mais sobre quais informações são necessárias para constituir uma correção, ou como / se isso pode ser recuperado de um GpsSatelliteobjeto.
Christopher Orr
Outro pensamento ... você não mencionou isso na sua pergunta, mas já tentou usar apenas LocationManager.requestLocationUpdatescom as configurações de tempo e distância definidas para 0? Isso deve enviar a você pontos de GPS assim que eles acontecerem. Se você não está recebendo nada, provavelmente não tem uma solução. Você pode combinar isso com o listener de status acima, se desejar.
Christopher Orr
1
Iterando "sats" e verificando usedInFix () no Gpsocolate talvez?
DeliriumTremens
8

Depois de alguns anos trabalhando com GPS no windows mobile, percebi que o conceito de "perder" um ponto de GPS pode ser subjetivo. Para simplesmente ouvir o que o GPS lhe diz, adicionar um NMEAListener e analisar a sentença dirá se a correção foi "válida" ou não. Consulte http://www.gpsinformation.org/dale/nmea.htm#GGA . Infelizmente, com alguns GPSes, este valor irá flutuar para frente e para trás, mesmo durante o curso normal de operação em uma área de "boa posição".

Assim, a outra solução é comparar a hora UTC da localização GPS com a hora do telefone (convertida para UTC). Se estiverem com uma certa diferença de tempo, você pode presumir que perdeu a posição GPS.

Justin Breitfeller
fonte
Você se importaria de explicar o método de comparação de tempo? Na resposta de Stephen Daye, ele comparou as diferenças de tempo entre quando a última correção aconteceu e o evento GPS_EVENT_SATELLITE_STATUS mais recente ... Não tenho certeza do que está medindo.
Rokey Ge
7

entrar em um problema semelhante ao trabalhar em meu projeto de MSc, parece que a resposta de Daye relatou erroneamente "nenhuma correção" enquanto o dispositivo permanece em um local estático. Modifiquei um pouco a solução, o que parece funcionar bem para mim em um local estático. Não sei como isso afetaria a bateria, pois não é minha principal preocupação, mas aqui está como fiz isso, solicitando novamente as atualizações de localização quando uma correção expirou.

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
fonte
5

Bem, reunir todas as abordagens de trabalho resultará nisso (também lidando com 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 resposta é uma combinação das respostas acima.

Gregory Stein
fonte
1
Contanto que você dê crédito a outro (s) autor (es), não há problema em obter seus próprios votos positivos para um trabalho novo ou derivado. As regras aqui estão mais preocupadas com as pessoas plagiando as respostas de outras pessoas e usando-as em outro lugar no Stack Overflow para atrair representantes desonestamente. Como você mencionou que baseou em outras respostas, acho que está bem.
Halfer
3

Se você só precisa saber se há uma correção, verifique a última localização conhecida fornecida pelo receptor GPS e verifique o valor .getTime () para saber quantos anos ele tem. Se for recente o suficiente (como ... alguns segundos), você tem uma correção.

   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, finalmente, compare com a data e hora atual (em UTC!). Se for recente o suficiente, você tem uma correção.

Eu faço isso no meu aplicativo e até agora tudo bem.

Adan
fonte
Parece que vale a pena experimentar o +1 se você quiser saber imediatamente se há uma solução.
AgentKnopf
2

Posso estar errado, mas parece que as pessoas estão saindo do assunto por

só preciso saber se o ícone do GPS na parte superior da tela está piscando (sem correção real)

Isso é facilmente feito com

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

Para ver se você tem uma solução sólida, as coisas ficam um pouco mais 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) {}
    };

Agora sempre que quiser ver se tem conserto?

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

Se você quiser ser sofisticado, você sempre pode ter um tempo limite usando System.currentTimeMillis () e loc.getTime ()

Funciona de forma confiável, pelo menos em um N1 desde 2.1.

Regomodo
fonte
3
Sua solução é muito complexa para o que faz. No entanto, faz muito pouco. E se depois que o GPS resolver minha localização eu for para a estação subterrânea, fazendo com que o GPS perca a correção?
meandre
1

Com LocationManager, você pode getLastKnownLocation () depois de getBestProvider (). Isso fornece um objeto Location, que tem os métodos getAccuracy () em metros e getTime () em milissegundos UTC

Isso fornece informações suficientes?

Ou talvez você possa iterar nos LocationProviders e descobrir se cada um atende aos Critérios (ACCURACY_COARSE)

Mark Borgerding
fonte
1

tantos posts ...

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

adicionar este código, com addGpsListener ... showMessageDialog ... apenas mostra uma janela de diálogo padrão com a string

fez o trabalho perfeitamente para mim :) muito obrigado: =) (sry para este post, ainda não posso votar)

cV2
fonte
Esta é apenas a primeira correção. E se você entrar em um túnel?
Leos Literak
1

Se você não precisa de uma atualização no mesmo instante em que a correção é perdida, você pode modificar a solução de Stephen Daye dessa forma, para que tenha um método que verifica se a correção ainda está presente.

Portanto, você pode apenas verificar sempre que precisar de alguns dados de GPS e não precisar desse GpsStatus.Listener.

As variáveis ​​"globais" são:

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

Este é o método que é chamado em "onLocationChanged ()" para lembrar a hora de atualização e a localização atual. Além de atualizar "isGpsFix":

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

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

        checkGpsFix(); // optional
    }

Esse método é chamado sempre que preciso saber se há um ponto de GPS:

private boolean checkGpsFix(){

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

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

Em minha implementação, primeiro executo checkGpsFix () e, se o resultado for verdadeiro, uso a variável "lastKnownLocation" como minha posição atual.

Sebastian Mauthofer
fonte
1

Eu sei que é um pouco tarde. No entanto, por que não usar o NMEAListener se você quiser saber se há uma correção. Pelo que li, o NMEAListener fornecerá as sentenças NMEA e, a partir daí, você escolherá a sentença correta.

A sentença RMC contém o status de correção que é A para OK ou V para aviso. A frase GGA contém a qualidade fixa (0 inválido, 1 GPS ou 2 DGPS)

Não posso oferecer nenhum código java porque estou apenas começando com o Android, mas fiz uma biblioteca GPS em C # para aplicativos do Windows, que pretendo usar com o Xamarin. Eu só encontrei este tópico porque estava procurando informações sobre o provedor.

Pelo que li até agora sobre o objeto Location, não estou muito confortável com métodos como getAccuracy () e hasAccuracy (). Estou acostumado a extrair das sentenças NMEA valores HDOP e VDOP para determinar o quão precisas são minhas correções. É bastante comum ter uma correção, mas ter um HDOP ruim, o que significa que sua precisão horizontal não é muito boa. Por exemplo, sentado em sua mesa depurando com um dispositivo GPS Bluetooth externo contra uma janela, é bem provável que você consiga uma correção, mas HDOP e VDOP muito ruins. Coloque seu dispositivo GPS em um vaso de flores externo ou algo semelhante ou adicione uma antena externa ao GPS e imediatamente você obterá bons valores de HDOP e VDOP.

user2153142
fonte
0

Talvez seja a melhor possibilidade criar um TimerTask que defina o local recebido para um determinado valor (nulo?) Regularmente. Se um novo valor for recebido pelo GPSListener, ele atualizará a localização com os dados atuais.

Acho que seria uma solução de trabalho.

nr1
fonte
0

Você diz que já tentou onStatusChanged (), mas isso funciona para mim.

Este é o método que uso (deixo a própria classe lidar com 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 eu lido com onStatusChanged da seguinte maneira:

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

Observe que os métodos onProvider {Dis, En} abled () são para habilitar e desabilitar o rastreamento GPS pelo usuário; não é o que você está procurando.

CvR
fonte
Infelizmente, muitas vezes não funciona. onLocationChanged () é chamado uma vez por segundo, mas onStatusChanged não é chamado. Às vezes, gostaria de poder apenas consultar o status atual em vez de esperar por uma notificação que pode não chegar por muito tempo ou nunca.
Edward Falk
2
Certo. Eu aprendi algumas coisas depois da resposta anterior, e uma é que nem todas as plataformas Android são feitas da mesma forma. Eu definitivamente vejo chamadas para onStatusChanged no meu HTC Hero e no meu Archos 5, mas não estou surpreso que isso não funcione em todos os lugares. Que tal o plano B: você usa o GPSStatusListener que é mostrado em outra resposta, e simplesmente vê se algum dos satélites retorna verdadeiro para usedInFix (). Na minha experiência, isso mais se aproxima do comportamento do ícone de GPS padrão. (Você já tentou encontrar a fonte que implementa esse ícone padrão, BTW?)
CvR
0

Definir o intervalo de tempo para verificar se há correção não é uma boa escolha .. percebi que onLocationChanged não é chamado se você não estiver se movendo .. o que é compreensível, já que a localização não está mudando :)

A melhor maneira seria, por exemplo:

  • intervalo de verificação até o último local recebido (em gpsStatusChanged)
  • se esse intervalo for superior a 15s, defina a variável: long_interval = true
  • remova o listener de localização e adicione-o novamente, geralmente, você obterá a posição atualizada se a localização realmente estiver disponível, se não - você provavelmente perdeu a localização
  • em onLocationChanged você acabou de definir long_interval como false ..
Hardy
fonte