O problema:
Obter a localização atual do usuário dentro de um limite o mais rápido possível e, ao mesmo tempo, economizar bateria.
Por que o problema é um problema:
Primeiro, o Android tem dois provedores; rede e GPS. Às vezes a rede é melhor e às vezes o GPS é melhor.
Por "melhor", quero dizer velocidade versus precisão.
Estou disposto a sacrificar alguns metros de precisão se conseguir obter a localização quase instantaneamente e sem ligar o GPS.
Em segundo lugar, se você solicitar atualizações para alterações no local, nada será enviado se o local atual estiver estável.
O Google tem um exemplo de como determinar a "melhor" localização aqui: http://developer.android.com/guide/topics/location/obtaining-user-location.html#BestEstimate
Mas acho que não é tão bom quanto deveria /poderia ser.
Estou meio confuso por que o google não possui uma API normalizada para localização, o desenvolvedor não precisa se preocupar com a localização, você deve especificar o que deseja e o telefone deve escolher.
Com o que preciso de ajuda:
Preciso encontrar uma boa maneira de determinar o "melhor" local, talvez com alguma heurística ou talvez através de uma biblioteca de terceiros.
Isso não significa determinar o melhor provedor!
Provavelmente vou usar todos os provedores e escolher o melhor deles.
Antecedentes da aplicação:
O aplicativo coletará a localização do usuário em um intervalo fixo (digamos a cada 10 minutos ou mais) e a enviará para um servidor.
O aplicativo deve economizar o máximo possível de bateria e o local deve ter uma precisão de X (50-100?) Metros.
O objetivo é mais tarde poder traçar o caminho do usuário durante o dia em um mapa, para que eu precise de precisão suficiente para isso.
Outros:
Na sua opinião, quais são os valores razoáveis das precisões aceitas e desejadas?
Eu tenho usado 100m como aceito e 30m como desejado, isso é pedir muito?
Gostaria de poder traçar o caminho do usuário em um mapa posteriormente.
100m para o desejado e 500m para o aceito são melhores?
Além disso, no momento, eu tenho o GPS ligado por no máximo 60 segundos por atualização de local. Isso é muito curto para obter um local se você estiver em um ambiente fechado com uma precisão de talvez 200 m?
Este é o meu código atual, qualquer comentário é apreciado (além da falta de verificação de erros, que é o TODO):
protected void runTask() {
final LocationManager locationManager = (LocationManager) context
.getSystemService(Context.LOCATION_SERVICE);
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER));
updateBestLocation(locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
Looper.prepare();
setLooper(Looper.myLooper());
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateBestLocation(location);
if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
return;
// We're done
Looper l = getLooper();
if (l != null) l.quit();
}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
public void onStatusChanged(String provider, int status,
Bundle extras) {
// TODO Auto-generated method stub
Log.i("LocationCollector", "Fail");
Looper l = getLooper();
if (l != null) l.quit();
}
};
// Register the listener with the Location Manager to receive
// location updates
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
Looper.myLooper());
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1000, 1,
locationListener, Looper.myLooper());
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
Looper l = getLooper();
if (l != null) l.quit();
// Log.i("LocationCollector",
// "Stopping collector due to timeout");
}
}, MAX_POLLING_TIME);
Looper.loop();
t.cancel();
locationManager.removeUpdates(locationListener);
setLooper(null);
}
if (getLocationQuality(bestLocation) != LocationQuality.BAD)
sendUpdate(locationToString(bestLocation));
else Log.w("LocationCollector", "Failed to get a location");
}
private enum LocationQuality {
BAD, ACCEPTED, GOOD;
public String toString() {
if (this == GOOD) return "Good";
else if (this == ACCEPTED) return "Accepted";
else return "Bad";
}
}
private LocationQuality getLocationQuality(Location location) {
if (location == null) return LocationQuality.BAD;
if (!location.hasAccuracy()) return LocationQuality.BAD;
long currentTime = System.currentTimeMillis();
if (currentTime - location.getTime() < MAX_AGE
&& location.getAccuracy() <= GOOD_ACCURACY)
return LocationQuality.GOOD;
if (location.getAccuracy() <= ACCEPTED_ACCURACY)
return LocationQuality.ACCEPTED;
return LocationQuality.BAD;
}
private synchronized void updateBestLocation(Location location) {
bestLocation = getBestLocation(location, bestLocation);
}
// Pretty much an unmodified version of googles example
protected Location getBestLocation(Location location,
Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return location;
}
if (location == null) return currentBestLocation;
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use
// the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return location;
// If the new location is more than two minutes older, it must be
// worse
} else if (isSignificantlyOlder) {
return currentBestLocation;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and
// accuracy
if (isMoreAccurate) {
return location;
} else if (isNewer && !isLessAccurate) {
return location;
} else if (isNewer && !isSignificantlyLessAccurate
&& isFromSameProvider) {
return location;
}
return bestLocation;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
fonte
Respostas:
Parece que estamos codificando o mesmo aplicativo ;-)
Aqui está minha implementação atual. Ainda estou na fase de teste beta do meu aplicativo para upload de GPS, então pode haver muitas melhorias possíveis. mas parece funcionar muito bem até agora.
Editar: aqui está a parte que solicita as atualizações periódicas dos provedores de localização:
Coisas a considerar:
não solicite atualizações de GPS com muita frequência, ele consome a energia da bateria. Atualmente, uso 30 min como padrão para meu aplicativo.
adicione uma verificação de 'distância mínima ao último local conhecido'. sem isso, seus pontos irão "pular" quando o GPS não estiver disponível e a localização estiver sendo triangulada das torres de celular. ou você pode verificar se o novo local está fora do valor de precisão do último local conhecido.
fonte
Para selecionar o provedor de local certo para o seu aplicativo, você pode usar os objetos Critérios :
Leia a documentação para requestLocationUpdates para obter mais detalhes sobre como os argumentos são levados em consideração:
Mais pensamentos
Criteria.ACCURACY_HIGH
critério deve fornecer erros abaixo de 100 m, o que não é tão bom quanto o GPS, mas atende às suas necessidades.fonte
Criteria
mas e se a localização de rede mais recente for incrível (ela pode saber através de wifi) e não demorar tempo ou bateria para obtê-la (getLastKnown), então os critérios provavelmente desconsiderarão isso e retornarão o GPS. Não acredito que o Google tornou isso difícil para os desenvolvedores.Respondendo aos dois primeiros pontos :
O GPS sempre fornecerá uma localização mais precisa, se estiver ativado e se não houver paredes espessas ao redor .
Se o local não mudou, você pode chamar getLastKnownLocation (String) e recuperar o local imediatamente.
Usando uma abordagem alternativa :
Você pode tentar colocar o ID da célula em uso ou todas as células vizinhas
Você pode consultar a localização da célula através de vários bancos de dados abertos (por exemplo, http://www.location-api.com/ ou http://opencellid.org/ )
A estratégia seria ler a lista de IDs da torre ao ler o local. Em seguida, na próxima consulta (10 minutos no seu aplicativo), leia-as novamente. Se pelo menos algumas torres são iguais, é seguro usá-lo
getLastKnownLocation(String)
. Se não estiverem, aguardeonLocationChanged()
. Isso evita a necessidade de um banco de dados de terceiros para o local. Você também pode tentar essa abordagem .fonte
Esta é a minha solução que funciona bastante bem:
fonte
A precisão da localização depende principalmente do provedor de localização usado:
Se você procura a precisão, o GPS é sua única opção.
Eu li um artigo muito informativo sobre isso aqui .
Quanto ao tempo limite do GPS - 60 segundos devem ser suficientes e, na maioria dos casos, até demais. Eu acho que 30 segundos é bom e, às vezes, menos de 5 segundos ...
se você precisar apenas de um único local, sugiro que, no seu
onLocationChanged
método, depois de receber uma atualização, cancele o registro do ouvinte e evite o uso desnecessário do GPS.fonte
Atualmente estou usando, já que isso é confiável para obter a localização e calcular a distância para meu aplicativo ...... estou usando isso para meu aplicativo de táxi.
use a API de fusão que o desenvolvedor do Google desenvolveu com a fusão de sensor GPS, magnetômetro e acelerômetro, que também usa Wi-Fi ou localização da célula para calcular ou estimar a localização. Também é capaz de fornecer atualizações de localização também dentro do edifício com precisão. para obter detalhes, acesse o link https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderApi
fonte
Procurei na Internet uma resposta atualizada (no ano passado) usando os métodos mais recentes de localização sugeridos pelo google (para usar o FusedLocationProviderClient). Finalmente cheguei a isso:
https://github.com/googlesamples/android-play-location/tree/master/LocationUpdates
Criei um novo projeto e copiei a maior parte desse código. Estrondo. Funciona. E acho que sem linhas obsoletas.
Além disso, o simulador não parece ter uma localização GPS, que eu saiba. Chegou ao ponto de relatar isso no log: "Todas as configurações de localização são satisfeitas".
E, finalmente, caso você queira saber (eu sabia), você NÃO precisa de uma chave API do google maps no console do desenvolvedor do google, se tudo o que você quer é a localização do GPS.
Também é útil o seu tutorial. Mas eu queria um tutorial completo de uma página / exemplo de código, e isso. O tutorial deles empilha, mas é confuso quando você é novo nisso, porque você não sabe quais são as peças necessárias nas páginas anteriores.
https://developer.android.com/training/location/index.html
E, finalmente, lembre-se de coisas assim:
Eu não apenas tive que modificar o mainActivity.Java. Também tive que modificar Strings.xml, androidmanifest.xml E o build.gradle correto. E também o activity_Main.xml (mas essa parte foi fácil para mim).
Eu precisava adicionar dependências como esta: implementação 'com.google.android.gms: play-services-location: 11.8.0' e atualizar as configurações do meu SDK do Android Studio para incluir os serviços do Google Play. (configurações do arquivo aparência configurações do sistema Android SDK SDK Tools verifique os serviços do Google Play).
update: o simulador android parecia obter um local e eventos de alteração de local (quando alterei o valor nas configurações do sim). Mas meus melhores e primeiros resultados foram em um dispositivo real. Portanto, é provavelmente mais fácil testar em dispositivos reais.
fonte
Recentemente refatorado para obter a localização do código, aprender algumas boas idéias e finalmente obter uma biblioteca e uma demonstração relativamente perfeitas.
@ A resposta de Gryphius é boa
Implementação completa: https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java
1.Obrigado pelas idéias da solução @Gryphius, também compartilho o código completo.
2.Cada solicitação para concluir o local, é melhor remover Atualizações, caso contrário, a barra de status do telefone sempre exibirá o ícone de posicionamento
fonte
Na minha experiência, achei melhor usar a correção GPS, a menos que ela não estivesse disponível. Não sei muito sobre outros provedores de localização, mas sei que para o GPS existem alguns truques que podem ser usados para dar uma medida de precisão do gueto. A altitude geralmente é um sinal, para que você possa verificar valores ridículos. Existe a medida de precisão nas correções de localização do Android. Além disso, se você pode ver o número de satélites usados, isso também pode indicar a precisão.
Uma maneira interessante de obter uma idéia melhor da precisão pode ser pedir um conjunto de correções muito rapidamente, como ~ 1 / s por 10 segundos e depois dormir por um minuto ou dois. Uma conversa em que estive levou a acreditar que alguns dispositivos Android farão isso de qualquer maneira. Você eliminaria os outliers (ouvi o filtro Kalman mencionado aqui) e usaria algum tipo de estratégia de centralização para obter uma única correção.
Obviamente, a profundidade que você chega aqui depende de quão difíceis são seus requisitos. Se você tem um requisito particularmente rígido para obter a melhor localização possível, acho que você descobrirá que o GPS e a localização da rede são tão parecidos quanto as maçãs e laranjas. Além disso, o GPS pode ser totalmente diferente de dispositivo para dispositivo.
fonte
A Skyhook (http://www.skyhookwireless.com/) possui um provedor de localização muito mais rápido que o padrão que o Google fornece. Pode ser o que você está procurando. Eu não sou afiliado a eles.
fonte