Boa maneira de obter a localização do usuário no Android
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.
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):
final LocationManager locationManager = (LocationManager) context
if (getLocationQuality(bestLocation) != LocationQuality.GOOD) {
if (getLocationQuality(bestLocation) != LocationQuality.GOOD)
Looper l = getLooper();
if (l != null) l.quit();
Log.i("LocationCollector", "Fail");
LocationManager.GPS_PROVIDER, 1000, 1, locationListener,
// Log.i("LocationCollector",
// "Stopping collector due to timeout");
}, MAX_POLLING_TIME);
if (getLocationQuality(bestLocation) != LocationQuality.BAD)
sendUpdate(locationToString(bestLocation));
else Log.w("LocationCollector", "Failed to get a location");
private LocationQuality getLocationQuality(Location location) {
if (currentTime - location.getTime() < MAX_AGE
&& location.getAccuracy() <= GOOD_ACCURACY)
if (location.getAccuracy() <= ACCEPTED_ACCURACY)
// Pretty much an unmodified version of googles example
android geolocation android-location
— Nicklas A.
Chegar bem tarde, mas o "Provedor de localização fundida", anunciado recentemente no IO 2013, parece atender a muitas de suas necessidades - developer.android.com/google/play-services/location.html
a última linha de getBestLocation () não deve ser: return currentBestLocation; em vez de retornar bestLocation ;?
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:
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.
— Gryphius
Na verdade, você nunca obtém um novo local, apenas usa locais existentes nas atualizações anteriores. Eu acho que esse código se beneficiaria muito ao adicionar um ouvinte que atualiza o local ativando o GPS de tempos em tempos.
desculpe, pensei que você estava interessado apenas na parte que seleciona o melhor de todos os locais disponíveis. Eu adicionei o código acima que solicita esses também. se um novo local GPS for recebido, ele será armazenado / enviado imediatamente. se eu receber uma atualização de local de rede, eu a armazeno para referência e 'espero' que eu receba uma atualização de GPS também até a próxima verificação de local.
Eu também tive um método stopRecording () que cancelou o timer. Eu finalmente mudou de um temporizador para um ScheduledThreadPoolExecutor, então stopRecording agora basicamente chama executor.shutdown () e de-registra todos os localização ouvintes de atualização
de acordo com o meu scm, stopRecording chamado apenas gpsTimer.cancel () e defina gps_recorder_running = false, assim como no seu caso, nenhuma limpeza dos ouvintes naquela época. o código atual mantém o controle de todos os ouvintes ativos em um vetor, eu não tinha isso quando escrevi essa resposta 1,5 anos atrás.
ele já está no github , mas não tenho certeza se ainda é a melhor maneira de fazer coisas sobre GPS hoje em dia. Afaik, eles fizeram muitas melhorias na API de localização desde que escrevi este código.
— Gryphius 18/03
Para selecionar o provedor de local certo para o seu aplicativo, você pode usar os objetos Critérios :
myCriteria.setAccuracy(Criteria.ACCURACY_HIGH);
// let Android select the right location provider for you
String myProvider = locationManager.getBestProvider(myCriteria, true);
// finally require updates at -at least- the desired rate
long minTimeMillis = 600000; // 600,000 milliseconds make 10 minutes
locationManager.requestLocationUpdates(myProvider,minTimeMillis,0,locationListener);
Leia a documentação para requestLocationUpdates para obter mais detalhes sobre como os argumentos são levados em consideração:
A frequência da notificação pode ser controlada usando os parâmetros minTime e minDistance. Se minTime for maior que 0, o LocationManager poderá descansar por milésimos de segundo minTime entre as atualizações de local para economizar energia. Se minDistance for maior que 0, um local será transmitido apenas se o dispositivo se mover por medidores de minDistância. Para obter notificações com a maior frequência possível, defina os dois parâmetros como 0.
Você pode monitorar a precisão dos objetos Location com Location.getAccuracy () , que retorna a precisão estimada da posição em metros.
o Criteria.ACCURACY_HIGHcritério deve fornecer erros abaixo de 100 m, o que não é tão bom quanto o GPS, mas atende às suas necessidades.
Você também precisa monitorar o status do seu provedor de localização e mudar para outro provedor, se ele ficar indisponível ou desabilitado pelo usuário.
O provedor passivo também pode ser uma boa opção para esse tipo de aplicativo: a idéia é usar as atualizações de local sempre que solicitadas por outro aplicativo e transmitidas por todo o sistema.
Eu examinei, Criteriamas 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.
Além de usar os Critérios, você pode, em cada atualização de local enviada pelo provedor selecionado, verificar o lastKnowLocation do provedor de GPS e compará-lo (precisão e data) à sua localização atual. Mas isso me parece um requisito interessante e não um requisito para suas especificações; se, às vezes, for alcançada uma precisão um pouco melhor, será realmente útil para seus usuários?
É isso que estou fazendo agora, o problema é que tenho dificuldade em descobrir se o último conhecimento é bom o suficiente. Também posso acrescentar que não preciso me limitar a um provedor, quanto mais eu usar, mais rápido conseguiria um bloqueio.
Lembre-se de que PASSIVE_PROVIDER requer API nível 8 ou superior.
@ Stéphane desculpe pela edição. Não cuide disso. Sua postagem está correta. Eu fiz essa edição por erro. Desculpe. Saudações.
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
TelephonyManager mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
GsmCellLocation loc = (GsmCellLocation) mTelephonyManager.getCellLocation();
Log.d ("CID", Integer.toString(loc.getCid()));
Log.d ("LAC", Integer.toString(loc.getLac()));
List<NeighboringCellInfo> list = mTelephonyManager.getNeighboringCellInfo ();
for (NeighboringCellInfo cell : list) {
Log.d ("CID", Integer.toString(cell.getCid()));
Log.d ("LAC", Integer.toString(cell.getLac()));
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, aguarde onLocationChanged(). Isso evita a necessidade de um banco de dados de terceiros para o local. Você também pode tentar essa abordagem .
— Aleadam
Sim, mas eu o problema surge se o lastKnownLocation for muito ruim. Preciso de uma boa maneira de decidir o melhor dos dois locais.
Você pode armazenar as informações das torres e verificar se essas torres foram alteradas. Se o fizeram, aguarde um novo local, caso contrário (ou se apenas alguns foram alterados), reutilize-o. Dessa forma, você evita comparar os locais da torre com um banco de dados.
— Aleadam 31/05
Usar torres me parece um grande exagero, mas é uma boa ideia.
@ Nicklas, o código não é mais complicado que isso. Você precisará de android.Manifest.permission # ACCESS_COARSE_UPDATES.
Sim, mas ainda preciso usar um serviço de terceiros e também preciso de uma maneira de decidir quando usar as informações da torre sobre os dados de localização, isso apenas adiciona uma camada extra de complexidade.
Esta é a minha solução que funciona bastante bem:
Oi Nicklas eu tenho mesmo equirement para que eu pudesse comunicar a você por qualquer meio .. i seria agradecer completo para você se você pudesse nos ajuda ..
— menino de escola
Você poderia postar o código inteiro? Obrigado, muito apreciado
Esse é todo o código. Não tenho mais acesso ao projeto.
Você parece ter pegado o código deste projeto "android-protips-location" e ele ainda está vivo. As pessoas podem ver como isso funciona aqui code.google.com/p/android-protips-location/source/browse/trunk/…
— Gödel77 4/14/14
A precisão da localização depende principalmente do provedor de localização usado:
GPS - você obtém vários metros de precisão (supondo que você tenha recepção GPS)
Wifi - você terá algumas centenas de metros de precisão
Rede celular - Você obterá resultados muito imprecisos (já vi um desvio de até 4 km ...)
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 onLocationChangedmétodo, depois de receber uma atualização, cancele o registro do ouvinte e evite o uso desnecessário do GPS.
Eu realmente não me importo de onde eu começar a minha localização, eu não quero me limitar a um provedor
Você pode registrar todos os provedores de localização disponíveis no dispositivo (você pode obter a lista de todos os provedores em LocationManager.getProviders ()), mas se estiver procurando uma correção precisa, na maioria dos casos o provedor de rede não será útil para você.
Sim, mas isso não é uma questão sobre escolher entre fornecedores, esta é uma pergunta sobre como obter o melhor localização em geral (mesmo quando há múltiplos fornecedores envolvidos)
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
— AshisParajuli
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:
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.
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.
— Neo42
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
//request all valid provider(network/gps)
private boolean requestAllProviderUpdates() {
checkRuntimeEnvironment();
if (isRequesting) {
EasyLog.d("Request location update is busy");
long minTime = getCheckTimeInterval();
float minDistance = getCheckMinDistance();
if (mMapLocationListeners == null) {
mMapLocationListeners = new HashMap<>();
mValidProviders = getValidProviders();
if (mValidProviders == null || mValidProviders.isEmpty()) {
throw new IllegalArgumentException("Not available provider.");
for (String provider : mValidProviders) {
EasyLog.e("LocationListener callback location is null.");
printf(location);
mLastProviderTimestamp = location.getTime();
finishResult(location);
doLocationResult(location);
removeProvider(location.getProvider());
if (isEmptyValidProviders()) {
requestTimeoutMsgInit();
removeUpdates();
getLocationManager().requestLocationUpdates(provider, minTime, minDistance, locationListener);
mMapLocationListeners.put(provider, locationListener);
EasyLog.d("Location request %s provider update.", provider);
isRequesting = true;
//remove request update
public void removeUpdates() {
LocationManager locationManager = getLocationManager();
if (mMapLocationListeners != null) {
Set<String> keys = mMapLocationListeners.keySet();
LocationListener locationListener = mMapLocationListeners.get(key);
EasyLog.d("Remove location update, provider is " + key);
mMapLocationListeners.clear();
isRequesting = false;
//Compared with the last successful position, to determine whether you need to filter
private boolean isNeedFilter(Location location) {
checkLocation(location);
float distance = location.distanceTo(mLastLocation);
if (distance < getCheckMinDistance()) {
if (location.getAccuracy() >= mLastLocation.getAccuracy()
&& distance < location.getAccuracy()) {
if (location.getTime() <= mLastProviderTimestamp) {
private void doLocationResult(Location location) {
if (isNeedFilter(location)) {
EasyLog.d("location need to filtered out, timestamp is " + location.getTime());
finishResult(mLastLocation);
//Return to the finished position
private void finishResult(Location location) {
if (mLocationResultListeners != null && !mLocationResultListeners.isEmpty()) {
String format = "Location result:<%f, %f> Accuracy:%f Time:%d Provider:%s";
EasyLog.i(String.format(format, latitude, longitude, accuracy, time, provider));
Iterator<LocationResultListener> iterator =  mLocationResultListeners.iterator();
LocationResultListener listener = iterator.next();
listener.onResult(location);
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
— Bingerz
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.
— rebocadores
Bem, não é importante que seja o melhor, apenas que seja bom o suficiente para traçar em um mapa e que eu não gaste a bateria, pois essa é uma tarefa em segundo plano.
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.
— Ed Burnette
Interessante, de fato, eles só parecem usar WiFi, no entanto, o que é muito bom, mas eu ainda preciso que ele funcione quando não há conexão Wi-Fi ou 3G / 2G por isso, isso adicionaria outra camada de abstração. Boa captura embora.
O Skyhook parece usar uma combinação de WiFi, GPS e torres de celular. Consulte skyhookwireless.com/howitworks para obter detalhes técnicos. Eles obtiveram várias vitórias de design recentemente, por exemplo, Mapquest, Twydroid, ShopSavvy e Sony NGP. Observe que o download e a experimentação do SDK deles parecem gratuitos, mas é necessário entrar em contato com eles sobre uma licença para distribuí-lo no seu aplicativo. Infelizmente, eles não listam o preço em seu site.