Tracking Com Location API, JobScheduler e Google Maps V2 no Android - Parte 4

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes! Você receberá um email de confirmação. Somente depois de confirma-lo é que eu poderei lhe enviar os conteúdos semanais exclusivos. Os artigos em PDF são entregues somente para os inscritos na lista.

Email inválido.
Blog /Android /Tracking Com Location API, JobScheduler e Google Maps V2 no Android - Parte 4

Tracking Com Location API, JobScheduler e Google Maps V2 no Android - Parte 4

Vinícius Thiengo
(5652) (11)
Go-ahead
"O método consciente de tentativa e erro é mais bem-sucedido que o planejamento de um gênio isolado."
Peter Skillman
Prototipagem Android
Capa do curso Prototipagem Profissional de Aplicativos
TítuloAndroid: Prototipagem Profissional de Aplicativos
CategoriasAndroid, Design, Protótipo
AutorVinícius Thiengo
Vídeo aulas186
Tempo15 horas
ExercíciosSim
CertificadoSim
Acessar Curso
Quer aprender a programar para Android? Acesse abaixo o curso gratuito no Blog.
Lendo
TítuloManual de DevOps: como obter agilidade, confiabilidade e segurança em organizações tecnológicas
CategoriaEngenharia de Software
Autor(es)Gene Kim, Jez Humble, John Willis, Patrick Debois
EditoraAlta Books
Edição
Ano2018
Páginas464
Conteúdo Exclusivo
Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba gratuitamente conteúdos Android sem precedentes!
Email inválido

Opa, blz?

Nesse vídeo mostro a implementação de um script de Tracking utilizando as tecnologias Location API, JobScheduler API, AsyncTask, EventBus lib e um Dashboard Web para também termos um acompanhamento remoto com uma página Web utilizando Ajax para atualização do Google Maps Web. Não há implementação como costumo fazer nos outros vídeos, porém acredito estar mais show de bola, pois apresento uma implementação utilizando também conteúdos que utilizei em outros vídeos anteriores a essa série do Location API e que são bastante úteis, como: SharedPreferences, AsyncTask, EventBus ("a" Lib do Android)... Fique atento quanto aos comentários que faço sobre a JobSchedulerCompat, pois há alguns problemas sérios nela, um deles é que o consumo de bateria em um dos casos de implementação passou dos 40% a noite o outro foi que eu testando aqui em meu aparelho real e emulador Genymotion ele rodava apenas a primeira vez, nas outras não funcionava, mas ai pode ser problema local (meus devices de teste), nesse caso vc teria de testar com os seus para ver se o problema continua.

Outra coisa a se notar é a não implementação do script de getLastLocation() que aparentemente pode parecer a implementação óbvia a ser utilizada até o momento que descobrimos que ela ou é muito lenta para atualizar sua instancia de last location ou não atualiza até a próxima construção da APP no device. Lembrando que há várias formas de realizar o tracking, uma um pouco mais óbvia é com a utilização de um Service, AlarmaManager e BroadcastReceiver, mas enfim, vou evitar mais delongas e deixar você assistir ao vídeo.

Obs. : O vídeo de cadastro de Places para utilizar no próximo vídeo de "Places Mais Próximos" será implementado ainda, então se baixar o código Web (HTML, JavaScript, PHP, ...) saiba que ainda falta essa parte.

O link para download do projeto, incluindo dados Web, estão logo abaixo no post.

Os links dos vídeos anteriores a essa série estão na "Relacionado" dessa página.

Segue abaixo os links dos vídeos das outras entidades uteis além das de maps utilizadas nesse vídeo:

Google Maps V2 no Android, Inicio e Configuração

Markers e Listeners no Google Maps Android

EventBus Lib, Comunicação Entre Entidades Android

SharedPreferences no Android, Entendendo e Utilizando

AsyncTask no Android, Acesso a Thread Principal de Forma Otimizada

Vlw

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes!
Email inválido

Relacionado

Obtendo Localização Com Location API no Android - Parte 1Obtendo Localização Com Location API no Android - Parte 1Android
Location API no Android, Atualização de Localização - Parte 2Location API no Android, Atualização de Localização - Parte 2Android
Obtendo Endereços Com Geocoder em Location API Android - Parte 3Obtendo Endereços Com Geocoder em Location API Android - Parte 3Android
JobScheduler API no Android, Entendendo e UtilizandoJobScheduler API no Android, Entendendo e UtilizandoAndroid

Compartilhar

Comentários Facebook

Comentários Blog (11)

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...
Ruan Alves (2) (0)
21/02/2017
Opa Blz? ... Surgiu uma dúvida aqui, necessito pegar a localização do usuário com precisão a cada 5 minutos e enviar para meu WebService, no caso estou usando GcmNetworkManager. No caso para uma precisão estou usando o LocationListener, so que percebi que ao fechar a aplicação e deixa la quieta, a mesma para com um determinado tempo, ou seja começo a andar de carro e ela não pega a localização dos 5 minutos, o problema não esta no GcmNetworkManager por que já testei essa parte, então a minha duvida é a seguinte tem como forçar? ou o android indentifica que a minha aplicação esta consumindo muito e ela da um stop? ou no caso ele deve estar com o aplicativo aberto seja qual for a Activity para poder pegar a localização ? ...
Responder
Vinícius Thiengo (0) (0)
24/02/2017
Ruan, tudo bem aqui.

Faça o seguinte teste: feche a aplicação, mas não pare de utilizar o device, digo, não deixe ele entrar em modo ?dormindo?. Veja se assim as coordenadas continuam sendo enviadas.

Lembrando que o GcmNetworkManager não vai trabalhar no tempo exato que você definiu, ele vai trabalhar em algum ponto dentro desse tempo.

Utilize o Log para saber se ao menos o GcmNetworkManager está sendo acionado. Digo isso, pois tem grandes possibilidades de você estar enfrentando as limitações empregadas pelo Doze Mode, isso para devices a partir do Android Marshmallow.

Você provavelmente já deve estar ciente sobre o Doze Mode. Caso seja confirmado esse o problema, das opções como solução veja se alguma das duas abaixo lhe ajuda:

- Abrir um dialog solicitando ao usuário que entre nas configurações do aparelho e libere o aplicativo em WhiteList, dessa forma o Doze Mode não tem efeito sobre ele. Informe ao usuário que caso ele não faça isso o aplicativo não funcionará na melhor performance;

- Trabalhar com um backend Web, onde na verdade você terá implementado um push notification Third-party Server (GCM / FCM) e também terá um crontab que invocará essa página do algoritmo da push notification a cada 5 minutos. A cada 5 minutos será enviada uma push notification para os devices que têm sua APP, uma push notification de alta prioridade, algo que o Doze Mode também não consegue bloquear. Os problemas dessa versão são: código não trivial, incluindo uso de um backend Web; o delay da push notification não está sobre seu controle, apesar de raro, pode uma sobrecarga nos push notification servers do Google e as mensagens de alta prioridade demorarem mais para serem entregues.

Caso seja identificado que o problema é mesmo com a LocationListener API, então é partir para a configuração do Location API com regra de negócio para alta prioridade na precisão da coordenada.

Com o problema sendo a LocationListener API, não vejo outra solução a não ser o uso da Location API.

Na pior das hipóteses você terá de criar uma lógica local em seu aplicativo para trabalhar as coordenadas retornadas pela Location API. Assim, de acordo com algumas verificações de tempo e coordenadas anteriores, definir se a coordenada atual é válida, caso não, solicitar alguma outra.

Abraço.
Responder
Ruan Alves (1) (0)
24/02/2017
Opa Show, eu testei em um tablet de android 4.4 e um moto G1 android 5.0, o que acontece ele chama sim o GCM, so que ao buscar as informações do GPS demora muito, o problema é que se o carro esta em movimento ele não pega fácil aquela localização, quando eu chamo o LocationListener ele faz a busca e depois eu dou o "RemoveUpdate" para parar o processo, ou eu uso o "Single" para pegar apenas uma vez ... Percebi que quando eu executo uma aplicação que não dou o "RemoveUpdate" ele atualiza mais rápido, mais no caso as informações são apresentadas na tela e não com a aplicação fechada ... Sobre o Location API, vi que uns dos problema é que ele pega muitas das vezes o "LAST" no caso tenho que validar aquela posição ... Sobre o Location API ele funciona como "service" direito? ou seja o APP fechado e o GCM chamando o mesmo para buscar a localização ...

Obs: para solucionar o meu problema por enquanto, estou priorizando a conexão com a Rede 3G, assim se existe essa conexão pego a localização por ela, se não uso o GPS ... Mais sempre usando o "LocationListener ", pelo jeito vou ter que implementar o Location API, parece que ele da mais suporte na precisão, unica coisa que bateu um duvida sobre o Location API, que não vou utilizar o Start e Stop, alguma observação sobre isso?
Responder
Vinícius Thiengo (1) (0)
24/02/2017
Ruan, o Location API funciona sim em um GCMNetworkManager.

Sobre o Start / Stop. Terá somente que realizar a requisição de coordenada e então trabalha-la quando recebida.

Para manter as requisições o código é diferente do de obter apenas uma. Esse de updates constantes você provavelmente não precisará. Abraço.
Responder
José Rodrigues (1) (0)
04/01/2016
Escrevi na publicação no face mais não apareceu se tiver duplicado me desculpe. Estou utilizando o Service para realizar Tracking Location com o Fused Provider. O meu problema é que as vezes ele vai fazendo a busca por localizações, mas de repente ele não pega mais, para no meio do caminho e não volta a pegar a localização, algumas "viagens" tem 100 km e ele marca somente 30 km, estou investigando porque não acontece toda hora. Segue o meu código, eu vou pegando a localização e salvando no banco sqlite. Se tiver alguma dica ou orientação de algo que eu possa estar fazendo errado fico agradecido t+.
public class TrackerService extends Service  implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,LocationListener{

private static final String LogCat = "DIGICERTO Track";
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
    // Location updates intervals in sec
private static int UPDATE_INTERVAL = 0; // 10 sec
private static int FATEST_INTERVAL = 0; // 5 sec
private static int DISPLACEMENT = 30; // 10 meters
private ArrayList storedLocation;
private boolean isTracking = false;
public Location mLastLocation;
private ViagemRepository viagemRepository;
private PadraoRepository padraoRepository;
private ParametroRepository parametroRepository;
private Padrao padrao;
private Arquivo arquivo;
private Parametro parametro;
String dataPadrao;
@Override
public void onCreate() {
super.onCreate();
padraoRepository = new PadraoRepository(getApplicationContext());
viagemRepository = new ViagemRepository(getApplicationContext());
parametroRepository = new ParametroRepository(getApplicationContext());


 parametro = parametroRepository.buscaPorId(1);
if(parametro.getTempoUpdateGps()> 0){
UPDATE_INTERVAL = parametro.getTempoUpdateGps();
FATEST_INTERVAL = parametro.getTempoUpdateGps()/2;
}else{
UPDATE_INTERVAL = 30000;
FATEST_INTERVAL = 15000;
}


callConnection();
createLocationRequest();
String log = "Reiniciou o serviço";
arquivo = new Arquivo(getApplicationContext(),parametro.getModoColeta().getModoColeta());
padraoRepository = verificaPadrao();

Padrao padrao = padraoRepository.ultimoPadrao();
 
log(log);

}


@Override
public void onDestroy() {
super.onDestroy();
//padraoRepository.fechar();
//viagemRepository.fechar();


if(mGoogleApiClient!=null){
stopLocationUpdates();
}

String log ="Finalizou serviço.....\r\n";
log(log);

}



public int onStartCommand(Intent intent,int flags,int startId){
if(intent != null){

}
return super.onStartCommand(intent, flags, startId);
}


public synchronized void callConnection(){
      mGoogleApiClient = new GoogleApiClient.Builder(this).
      addOnConnectionFailedListener(this)
      .addConnectionCallbacks(this)
      .addApi(LocationServices.API)
      .build();
      mGoogleApiClient.connect();
     }
    

public void createLocationRequest() {
storedLocation = new ArrayList();

mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FATEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
//mLocationRequest.setSmallestDisplacement(DISPLACEMENT);
}

public void startTracking(){
startLocationUpdates();
Log.i(LogCat, "Tracking iniciado......");

String log =" Tempo maximo: "+UPDATE_INTERVAL+" Tempo Minimo: "+FATEST_INTERVAL+"\r\n";

Log.i("DIGICERTO","Tempo maximo: "+UPDATE_INTERVAL+" Tempo Minimo: "+FATEST_INTERVAL);
Log.i("DIGICERTO", "Tracking serviço rodando......");
log += "Tracking serviço rodando......\r\n";

isTracking = true;
//timer.scheduleAtFixedRate(new mainTask(),0,UPDATE_INTERVAL);
log(log);
}

public void stopTracking(){
stopLocationUpdates();
Log.i(LogCat, "Parando Tracking");

isTracking = false;

String log = " Parando Tracking\r\n";
log(log);


}

public boolean isTracking(){
return isTracking;
}

protected void startLocationUpdates() {
//createLocationRequest();
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);

String log = " Iniciou updates de localizacoesr\n";
log(log);
}

protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);

String log = " Parou updates de localizacoes.\r\n";
log(log);

}

@Override
public void onLocationChanged(Location location) {
if (location != null) {
Log.i(LogCat, "Novo Local Adicionado: " + location.getLatitude()
+ " " + location.getLongitude());
storedLocation.add(location);
mLastLocation = location;

padraoRepository = verificaPadrao();

if (padraoRepository.ultimoPadrao() != null) {
salvarLocalizacoesViagem(location,
padraoRepository.ultimoPadrao());
}

} 

}

private PadraoRepository verificaPadrao(){
PadraoRepository pad;
if(padraoRepository==null){
pad = new PadraoRepository(getApplicationContext());
}else{
pad = padraoRepository;
}
return pad;
}

private ViagemRepository verificaViagem(){
ViagemRepository viag;
if(viagemRepository == null){
viag = new ViagemRepository(getApplicationContext());
}else{
viag = viagemRepository;
}
return viag;
}

private void salvarLocalizacoesViagem(Location location, Padrao padrao) {
viagemRepository = verificaViagem();
Viagem ultimaViagem = viagemRepository
.listarUltimoRegistroViagem(padrao);

double totalDistance = 0.0;
GPSUtil gps = new GPSUtil();

Viagem viagem = new Viagem();
viagem.setPadrao(padrao);
Calendar calendar = Calendar.getInstance();
viagem.setDataHora(calendar);

if (location != null) {
viagem.setLatitude(location.getLatitude());
viagem.setLongitude(location.getLongitude());
viagem.setVelocidade(location.getSpeed());

if (ultimaViagem.getLatitude() != 0
&& ultimaViagem.getLongitude() != 0) {

double distancia = gps.calcGeoDistance(
ultimaViagem.getLatitude(),
ultimaViagem.getLongitude(), location.getLatitude(),
location.getLongitude())
* gps.multipliers[2];
Location antigolocation = new Location("");
antigolocation.setLatitude(ultimaViagem.getLatitude());
antigolocation.setLongitude(ultimaViagem.getLongitude());

float distancia2 = location.distanceTo(antigolocation);

Log.d("DIGICERTO", "Latitude: " + ultimaViagem.getLatitude()
+ " " + ultimaViagem.getLongitude() + "\nULTIMA: "
+ ultimaViagem.getKmPercorrido());

Log.d("DIGICERTO",
"DataHora:"
+ DateUtil.timeMilisToString(viagem
.getDataHora().getTimeInMillis(),
"dd-MM-yy HH:mm")
+ "\n"
+ "DATA HORA GPS: "
+ DateUtil.timeMilisToString(location.getTime())
+ "\n" + "Latitude: " + viagem.getLatitude()
+ "\n" + "Longitude: " + viagem.getLongitude()
+ "\n" + "Velocidade: "
+ viagem.getVelocidade() * 2.2369 + "\n"
+ "\n" + "Acurracy: " + location.getAccuracy()
+ "\n"

);

Log.d("DIGICERTO", "Distancia: " + distancia + " " + distancia2);

if (distancia > 0.010 && location.getAccuracy() < 500.0f) {
totalDistance = ultimaViagem.getKmPercorrido() + distancia;
viagem.setKmPercorrido(totalDistance);
Log.d("DIGICERTO", "SOMADO: " + totalDistance);
viagemRepository.inserir(viagem);
}
} else {
double distancia = gps.calcGeoDistance(padrao.getLatitude(),
padrao.getLongitude(), location.getLatitude(),
location.getLongitude())
* gps.multipliers[2];
viagem.setKmPercorrido(distancia);
viagemRepository.inserir(viagem);
}

} else {
viagem.setKmPercorrido(ultimaViagem.getKmPercorrido());
viagem.setLatitude(ultimaViagem.getLatitude());
viagem.setLongitude(ultimaViagem.getLatitude());
viagem.setVelocidade(0);
viagemRepository.inserir(viagem);
}

}

public int getLocationCount(){
return storedLocation.size();
}

public Location getLocation(){
return mLastLocation;
}

public ArrayList getListLocation(){
return storedLocation;
}

    
@Override
public IBinder onBind(Intent intent) {
    return new TrackerBinder(this);
}


@Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(LogCat, "falha location: "+result.getErrorCode());

String log =" Falha location  "+result +"+\r\n";
log(log);

}


@Override
public void onConnected(Bundle connectionHint) {
Log.i(LogCat, "connectado");
String log =" Conectou com o gps\r\n";

Location location = LocationServices
                .FusedLocationApi
                .getLastLocation(mGoogleApiClient);

startLocationUpdates();
        if(location != null){
            log +=location.getLatitude() + " "+location.getLongitude()+"if(location != null)\r\n";           
        }else{
        log +="Erro na localizacao\r\n";
        }
        log(log);        

}



@Override
public void onConnectionSuspended(int cause) {
mGoogleApiClient.connect();
Log.i(LogCat, "suspenso - inicado");
String log = "suspenso - inicado "+cause;

log(log);

}


private void log(String newLog){

Calendar calendar = Calendar.getInstance();
String data = DateUtil.timeMilisToString(calendar.getTimeInMillis(),"dd-MM-yyyy hh:mm");
String log = data+" "+newLog;
try {
arquivo.WriteFile(log,parametro.getPastaDigiCerto(),"log","log.ath");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


}

Responder
Vinícius Thiengo (0) (0)
09/01/2016
Fala José Rodrigues, blz?
Uma dica que dou é tentar uma variação no código utilizando o LocationManager e o LocationListener (http://www.thiengo.com.br/gps-triangulacao-de-antenas-e-locationsource-no-android ) junto ao Service, pois já tive "n" queixas sobre a eficácia do Location API e todas no mesmo contexto, paravam ou nem mesmo iniciavam a busca por coordenadas. Tenha em mente tb que se o tracking estiver rodando quando a APP não está mais no foreground (primeiro plano) o Android pode remove-la a qualquer momento (o service, no caso) e voltar ou não dependendo do intervalo e do que o user está fazendo na APP.

Uma alternativa é implementar o tracking junto ao AlarmManager (http://www.thiengo.com.br/alarmmanager-no-android-sua-app-executando-em-tempos-definidos ) tb, dessa forma o Service não fica no background rodando de forma indefinida, roda apenas quando o AlarmManager chama-lo. Assim depois de pegar a coordenada ele finaliza novamente. Tente primeiro dessa forma, junto ao AlarmManager, pode ser o suficiente. Abraço
Responder
11/01/2016
Obrigado por responder, andei pesquisando pelo Alarm Manager, mais fiquei em dúvida pelo seguinte: como eu gero um mapa através das localizações geradas quanto mais localização eu tiver melhor para o mapa, seria certo eu iniciar o alarm manager de 30 em 30 segundos e fechá-lo ? Sobre esse código, realizei diversas mudanças uma delas foi reduzir o número de acesso ao banco de dados assim optei por usar Shared Preferences, e aparentemente melhorou o resultado do service. Além disso para mostrar a atualização de metragem, antes eu utilizava o TimerTask, mais vi que ele gasta muito recurso, e então agora estou utlizando o Handler e assim tive uma melhora de performance. Irei realizar um teste com o Alarm Manager. Obrigado.
Responder
Vinícius Thiengo (0) (0)
12/01/2016
Jose, se for já para utilizar no mapa, rode em uma Thread de background, pois a APP estará no foreground. A ideia do AlarmManager era em caso de que sua APP precisasse obter as coordenadas no background para salvar local ou enviar para um server web por exemplo... essa feature é comum em APPs que colhem informações enquanto estão "fechadas". Tenta a estratégia dos 30 segundos com o Handler que já está utilizando. Abraço
Responder
12/01/2016
Na verdade só mostro na tela os "km" percorridos, e vou fazendo como você falou, inserindo no banco de dados as latitudes e longitudes, depois de finalizado a viagem gero um arquivo ".kml".
No caso eu iria iniciar o alarm manager  a cada 30 segundos e depois de obter a localização fecho ele? ou deixo ele buscando a cada 30 segundos até o usuário fechar a viagem?
Responder
Vinícius Thiengo (0) (0)
13/01/2016
Jose, não tem melhor escolha ai sem testar antes. Veja com qual opção o Android responde com mais consistência com o decorrer do tempo, pois é ai que é critico, manter uma atividade de background depois de certo tempo de processamento. Abraço
Responder
13/01/2016
Blz agora é testar rs, fiz de forma que o service fica rodando, fiz um teste com o service direto e já está em 17 hrs no background, talvez funcione devido que o aparelho será usado somente para o uso do app. Obrigado por tirar as dúvidas.
Responder