GCMNetworkManager Para Execução de Tarefas no Background Android

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 /GCMNetworkManager Para Execução de Tarefas no Background Android

GCMNetworkManager Para Execução de Tarefas no Background Android

Vinícius Thiengo
(5225) (19)
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

Tudo bem?

Neste artigo vamos construir, passo a passo, um aplicativo Android de GPS Tracking, isso com o objetivo de mostrar uma maneira eficiente de trabalhar com tarefas em background, utilizando o GCMNetworkManager API.

No decorrer do conteúdo, além de outros assuntos, também serão abordadas as opções ante ao uso do GCMNetworkManager (AlarmManager, JobScheduler e SyncAdapter), incluindo as vantagens desse, o GCMNetworkManager, sobre essas.

Antes de prosseguir, não esqueça de se inscrever 📫 na lista de e-mails do Blog para receber todas as atualizações do desenvolvimento Android, em primeira mão.

Abaixo os tópicos apresentados no artigo:

Entendendo o Doze Mode

O Doze Mode foi incluído no Android a partir da API 23, Android 6 ou Android Marshmallow. O objetivo dessa entidade é aumentar o tempo de vida da bateria do device.

Não é de hoje que os smartphones, não somente Android, têm problemas com o tempo de vida de suas baterias.

Dê uma olhada na figura abaixo para entender melhor o Doze Mode:

Gráfico da janela de processamento no Doze Mode Android

Acima a representação do Doze Mode em ação. As barras laranjas são as várias aplicações do device realizando suas sincronizações com suas APIs Web, requisições Web.

Não sei se já está ciente do problema, mas o procedimento de ativar as tecnologias de radio do device para permitir a conexão com a Internet é um procedimento desgastante e que consome muito da bateria.

É exatamente nesse tempo que mais se consome a carga dela, isso quando no contexto: conexão com a Internet.

Para minimizar esse problema o Doze Mode entra em ação assim que o Android OS percebe que o device já está a algum tempo sem a iteração do usuário: tela em off por um longo tempo e device não ligado ao carregador.

As faixas verdes da figura anterior representam os momentos onde o device para com as execuções em background, incluindo as conexões com a Internet.

As barras laranjas que se espaçam cada vez mais são as janelas de manutenção (maintenance window). Nesses momentos os aplicativos rapidamente voltam a executar em background, podendo utilizar também a conexão com a Internet.

Logo depois, à volta ao Doze Mode, até o usuário entrar em ação no device ou alguma notificação de alta prioridade ser recebida.

Na figura anterior, a versão do Doze Mode apresentada é a mais crítica, Deep-Doze, com ela, a cada nova entrada em Doze Mode o espaço para a próxima janela de manutenção aumenta ainda mais.

Existe outro modo?

Sim, esse foi adicionado somente no Android 7 (ou Android Nougat). É o Light-Doze:

Gráfico da janela de processamento no Light-Doze Android

Com as mesmas condições: usuário sem iterar com o device por um longo tempo e o aparelho não está carregando.

A partir do Android 7 o device entra primeiro em Light-Doze, com a principal diferença sendo o não espaçamento entre as janelas de manutenção.

Porém fique ciente que o Light-Doze é apenas o pré-requisito para a entrada em Deep-Doze, pois esse ainda existe no Android 7 e é iniciado depois de um certo tempo em Light-Doze.

Ok, mas por que a explicação do Doze Mode nesse conteúdo sobre GCMNetworkManager?

Simples. O GCMNetworkManager é diretamente afetado pelo Doze Mode. Não somente esse, mas o JobScheduler, SyncAdapterAlarmManager também.

Imagine que você precise que seu aplicativo envie as coordenadas do usuário para sua aplicação Web de dez em dez minutos, sendo que é aceitável uma variação nesse intervalo, não tão grande, mas aceitável.

O GCMNetworkManager se encaixa perfeitamente como uma solução. Porém, em seus testes, você percebe que ao dormir (tem developer que faz isso, não vejo problemas), o número de envios de dados na madrugada (enquanto você e o device dormiam) caiu consideravelmente.

Logo, você corre para vários fóruns para tentar descobrir qual é o problema... e ... não é um problema, é uma característica dos devices Android, Doze Mode em ação.

Sua lógica de negócio precisa ser redesenhada ou você terá de utilizar outras tecnologias para conseguir esse feito: as coordenadas GPS do device a cada dez minutos.

Mas há solução para isso? Eu tenho um aplicativo que precisa de tarefas no background executando mesmo quando o usuário não está a algum tempo utilizando o device?

Se suas tarefas não envolvem conexão com a Internet, você pode utilizar o AlarmManager, pois segundo a documentação, o AlarmManager executa mesmo quando em Doze Mode.

Isso, pois seria muito "estranho" seu aplicativo de agendamento para tomar remédios, digo, agendamento de horários para tomar remédios, não funcionar devido ao usuário estar dormindo e não ter interagido com o device. Muito estranho!

Porém, se seu app precisa de conexão com a Internet, o AlarmManager não ajudará, pois nem mesmo ele conseguirá forçar a conexão com a Internet quando o device estiver em Doze Mode.

Nesse caso, a solução mais evidente pede um pouco mais de código.

Sua aplicação terá de ter um servidor Web onde rodará um crontab. Esse crontab acionará de tempos em tempos (de um em um minuto, por exemplo) um script back-end de seu projeto.

Esse script acessará sua base de dados de agendamentos. E então obterá todos os agendamentos para aquelas hora e minuto, enviará uma push message (notificação) com prioridade alta para os devices que precisam executar alguma tarefa relacionada ao agendamento de seus usuários.

Devido ao uso de prioridade alta em seus algoritmos de notificação (Google Cloud Messaging - GCM - ou Firebase Cloud Messaging - FCM) o Doze Mode não tem poder para bloquear essas notificações, fazendo com que a conexão com a Internet seja criada e que as tarefas de seu aplicativo possam ser executadas.

Quer um exemplo simples de aplicativos que utilizam notificações com alta prioridade? Aplicativos de mensagens.

Note que apesar da complexidade da solução anterior fazer com que ela pareça definitiva, na verdade ela não é.

Lembre que nós não temos controle sobre a latência da rede e nem sobre a situação dos servidores de push message do Google Android.

Ou seja, pode haver delay na entrega das notificações... perdeu-se o horário para tomar o remédio!

Com isso, a melhor solução é adaptar sua lógica de negócio. Na próxima seção há uma discussão falando sobre quando como utilizar as tecnologias de tarefas de background disponíveis no Android.

Importante: não deixe de ler a documentação sobre o Doze Mode, ela está em portuguêsOtimização para soneca e aplicativo em espera.

Nesse conteúdo há muito mais detalhes, incluindo algumas restrições que são aplicadas até mesmo ao AlarmManager e a aplicações que estão na lista de permissão para não entrarem na lista de aplicativos que devem respeitar o estado Doze Mode estritamente.

AlarmManager x JobScheduler x GCMNetworkManager x SyncAdapter

AlarmManager: a tecnologia de agendamento de tarefas de background mais simples de utilizar. O principal indício de que o AlarmManager é a escolha ideal é quando sua lógica de negócio precisa de exatidão na ativação da tarefa, ou seja, o que foi colocado na configuração, o tempo definido, deve ser respeitado.

Aplicativos de agendamento e de calendário de consultas são exemplos de apps que têm lógicas de negócio que não podem esperar condições ideais do device para terem suas tarefas executadas.

Porém, se seu aplicativo precisa de exatidão e ao mesmo tempo de conexão com a Internet, o recomendado é alterar o modelo de trabalho do app, pois com o que o Android lhe prover atualmente não será possível, sem a intervenção do usuário, garantir essas duas características ao mesmo tempo.

Sem a intervenção do usuário?

Sim, ele pode aumentar as prioridades de seu aplicativo nas configurações do device, mais precisamente, seu app pode avisa-lo sobre a necessidade atual utilizando a permissão REQUEST_IGNORE_BATTERY_OPTIMIZATIONS.

Mas não conte com esse comportamento, a não ser que seu aplicativo seja para os funcionários de sua empresa, onde você poderá também, pessoalmente, informa-los sobre a configuração requerida no device.

JobScheduler: se seu app não trabalha com devices com a API abaixo da 21, Lollipop, e também não precisa de exatidão na ativação das tarefas, apenas uma janela onde a execução é aceitável naquele tempo. Nessas condições o JobScheduler é uma excelente opção.

Aplicativos modernos que trabalham com GPS tracking se beneficiam com o uso do JobScheduler. Porém o principal ponto negativo é poder trabalhar apenas com devices que estejam com a API 21 ou superior.

SyncAdapter: uma excelente opção caso você não queira que seu aplicativo se preocupe com a utilização ou não do Google Play Services, além de dar suporte a partir da API 7. O SyncAdapter é antigo no Android e não muito famoso, digo, não entre os tutoriais gratuitos.

Por que isso?

O SyncAdapter não tem uma implementação trivial como as outras opções dessa seção. Para utiliza-lo você também tem que configurar um ContentProvider e o AccountManager, mesmo que de maneira fake, mock.

GCMNetworkManager: com características muito similares ao JobScheduler, o GCMNetworkManager se destaca em poder trabalhar também com APIs abaixo da 21. Além de já entrar em uma Thread de background quando invoca o método que deve acionar as tarefas que serão executadas, algo que o JobScheduler não faz.

Para APIs abaixo da 21 o GCMNetworkManager utiliza uma tecnologia proprietária do Google para manter o funcionamento sem diferença das APIs iguais ou maiores que a 21.

Para as APIs a partir da 21 o GCMNetworkManager utiliza o JobScheduler no background. A partir da API 9 você já consegue utilizar o GCMNetworkManager.

Com exceção do caso de necessidade de exatidão na ativação da tarefa, para todos os outros o GCMNetworkManager se encaixa muito bem.

Com as informações apresentadas até aqui você consegue definir o que utilizar. Não deixe de sempre priorizar a experiência do usuário de seu app, esqueça as dicas do Google se os números de seu analytics mostram que há mais iteração e retenção quando as tarefas executam com maior frequência. Continue assim, e vice-versa.

GCMNetworkManager, visão geral

Como já explicado anteriormente, o GCMNetworkManager não é exato quanto o tempo da execução da tarefa, definimos uma janela possível para execução, porém temos um ganho na otimização do uso da bateria e um código muito simples de se trabalhar.

Para utilizar o GCMNetworkManager você precisa primeiro adicionar a seguinte referência em seu Gradle App Level ou build.gradle (Module: app):

dependencies {
compile 'com.google.android.gms:play-services-gcm:10.0.0'
}

 

Mas o GCM não está para ser removido?

Na verdade não, não exatamente. Na documentação do GCM "push message" tem as recomendações para utilizar o FCM, pois esse último é mais atual e robusto.

De qualquer forma, o GCMNetworkManager é uma opção que, a princípio, não tem substituto no conjunto de tecnologias Firebase, logo as recomendações nos topos das páginas da documentação do GCM não valem para ele.

Continuando com a configuração padrão do GCMNetworkManager, é preciso um Service, mais precisamente um Service que herde de GcmTaskService:

public class CustomService extends GcmTaskService {

@Override
public void onInitializeTasks() {
super.onInitializeTasks();
}

@Override
public int onRunTask( TaskParams taskParams ) {
return GcmNetworkManager.RESULT_SUCCESS;
}
}

 

O método onInitializeTasks() é ativado quando o app é atualizado ou reinstalado. Nesse caso, quando esse método é invocado isso indica que todas as tarefas do aplicativo que tinham sido criadas e ainda não executadas foram destruídas.

É nesse método que seus algoritmos de lógica de negócio devem acessar sua base de dados local (ou remota) que tenha todas as configurações das tarefas que tinham sido agendadas e então reconstruí-las novamente.

Note que onInitializeTasks() roda na Thread principal enquanto onRunTask() roda em uma Thread de background.

Esse último, onRunTask(), é onde suas tarefas deverão ser executadas. Elas têm no máximo três minutos para serem executadas, depois disso a Thread é descartada.

Se os três minutos não forem o suficiente, parta para a velha e conhecida solução de: utilize um AlarmManager para criar um Service que no background realizará essa longa execução.

Voltando ao onRunTask(), nele você deve retornar algum dos seguintes três valores:

  • GcmNetworkManager.RESULT_SUCCESS: informa que a execução foi sem problemas e que essa tarefa atual não precisa ser executada novamente;
  • GcmNetworkManager.RESULT_FAILURE: informa que a tarefa falhou, mas que não precisa ser executada uma outra vez;
  • GcmNetworkManager.RESULT_RESCHEDULE: informa que houve falha na execução da tarefa e que essa deve ser reagendada para um nova execução.

Uma coisa importante: você vai ver que com o GCMNetworkManager nós podemos criar acionadores de tarefas (ou apenas tarefas, são sinônimos aqui) de um único trigger ou de triggers repetitivos.

Para os repetitivos, não confundam o retorno GcmNetworkManager.RESULT_RESCHEDULE como sendo necessário.

O retorno de onRunTask() é referente a tarefa atual em execução, nada tem relação com as tarefas posteriores de um trigger repetitivo.

Com isso podemos mostrar as configurações necessárias no AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.thiengo.gpstrackinggcmnetworkmanager">

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

...

<service
android:name=".service.CustomService"
android:exported="true"
android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">

<intent-filter>
<action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
</intent-filter>
</service>

</application>
</manifest>

 

Ok, a tag <service> já uma conhecida antiga minha, mas e essas configurações?

As tarefas que você criar com o GCMNetworkManager, digo, esses triggers (ou acionadores), são controlados pelo Google Play Services, ou seja, ele é que vai invocar o Service de sua aplicação.

O action com.google.android.gms.gcm.ACTION_TASK_READY é que vai permitir que o Service de sua aplicação responda ao trigger de tarefa do Google Play Services.

O atributo android:exported="true" permite que outra aplicação acione o Service de seu aplicativo, nesse caso a outra aplicação é o Google Play Services.

A permission com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE é a que permite que somente o Google Play Services seja a outra aplicação que pode acionar o service de seu aplicativo.

A permissão android.permission.RECEIVE_BOOT_COMPLETED é para que seja possível persistir com as tarefas já agendadas para seu app. Persistir depois de o device ter sido reiniciado.

Com o AlarmManager temos a dor de cabeça de termos de trabalhar com um BroadcastReciever para remontar todos os agendamentos.

Agora podemos passar para a criação da tarefa (ou, trigger de tarefa). Na MainActivity, por exemplo, você pode colocar o seguinte código:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );

OneoffTask task = new OneoffTask.Builder()
.setTag( "periodic" )
.setService( CustomService.class )
.setExecutionWindow( 5, 30 )
.setRequiredNetwork( Task.NETWORK_STATE_CONNECTED )
.setRequiresCharging( false )
.setUpdateCurrent( true )
.setPersisted( true )
.build();

GcmNetworkManager
.getInstance( this )
.schedule( task );
}
}

 

OneoffTask é a tarefa sem periodicidade, ela é chamada apenas uma vez. O schedule() do GcmNetworkManager é que agenda a tarefa. Quanto aos métodos:

  • setTag(): é o onde se defini o nome da tarefa, essa é a maneira de identifica-la;
  • setService(): aqui se defini o Service que executará a tarefa;
  • setExecutionWindow(): o primeiro parâmetro informa a partir de que tempo, em segundos, a tarefa já pode ser executada. O segundo parâmetro informa o segundo máximo, depois de a tarefa ter sido agendada, que ela já deverá ter entrado em execução;
  • setRequiredNetwork(): defini a condição requerida quanto a Internet no device, para poder executar:
    • Task.NETWORK_STATE_CONNECTED: deve haver conexão;
    • Task.NETWORK_STATE_ANY: qualquer estado de Internet é o suficiente;
    • Task.NETWORK_STATE_UNMETERED: que a tarefa só será executada se houver uma conexão de rede sem medição.
  • setRequiresCharging(): se é necessário o device estar sendo carregado (true) ou não (false) para que a tarefa seja acionada;
  • setUpdateCurrent(): caso a tarefa atual sendo criada tenha o mesmo tag name de uma outra tarefa na fila para execução, com esse método sendo true, a tarefa antiga na fila será descartada, caso contrário ambas vão ficar na fila aguardando a execução. Tarefas em execução não são canceladas;
  • setPersisted(): quando true e com a permissão android.permission.RECEIVE_BOOT_COMPLETED definida no AndroidManifest, faz com que todas as tarefas agendadas persistam depois da reinicialização do device;
  • build(): constrói a tarefa com as configurações informadas.

Para a execução de tarefas de forma repetitiva, você utiliza o PeriodicTask:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );

Bundle bundle = new Bundle();
bundle.putString( "key", "valor de teste" );

PeriodicTask task = new PeriodicTask.Builder()
.setTag( "periodic" )
.setService( CustomService.class )
.setPeriod( 20 )
.setFlex( 5 )
.setRequiredNetwork( Task.NETWORK_STATE_UNMETERED )
.setRequiresCharging( false )
.setUpdateCurrent( true )
.setPersisted( true )
.setExtras( bundle )
.build();

GcmNetworkManager
.getInstance( this )
.schedule( task );
}
}

 

Abaixo vamos a explicação dos método ainda não falados sobre. Segue:

  • setPeriod(): definimos, em segundos, o intervalo onde queremos que seja executada nossa tarefa, no máximo, uma vez. Esse período pode não ser respeitado algumas vezes;
  • setFlex(): faz com que seja criada uma janela de execução para a tarefa. Ou seja, com setPeriod(20) e setFlex(5) estamos informando ao Google Play Services que a tarefa deve ser invocada, preferencialmente, entre os segundo 15 e 20 do intervalo informado;
  • setExtras(): também disponível para o OneoffTask, nele podemos enviar dados que serão acessados por meio do parâmetro TaskParams de onRunTask() do GcmTaskService.

Para cancelar todas as tarefas criadas temos a seguinte opção:

...
GcmNetworkManager
.getInstance( this )
.cancelAllTasks( CustomService.class );
...

 

cancelAllTasks() recebe como parâmetro o Service que terá as tarefas a ele vinculadas removidas da fila para execução.

A outra opção é cancelTask():

...
GcmNetworkManager
.getInstance( this )
.cancelTask( "tag-name", CustomService.class );
...

 

Junto a tag name da tarefa, definindo o Service, é possível desativar uma task especifica.

Com isso cobrimos o "como" utilizar o GCMNetworkManager. Note que o Google Play Services vai acionar as tarefas em momentos oportunos, quando um app já estiver utilizando a conexão com a Internet, por exemplo, e se essa configuração de Internet estiver nas configurações de sua tarefa.

Quanto a configuração e momento oportuno, vale o mesmo para quando o device estiver carregando.

Com isso podemos passar ao exemplo, o aplicativo de GPS Tracking.

Projeto de exemplo - parte Web

Esse projeto Android tem uma simples parte Web onde as coordenadas são salvas em um arquivo .txt para simular um banco de dados relacional.

Para baixar esse projeto Web completo, acesse o GitHub dele em: https://github.com/viniciusthiengo/gps-tracking-gcm-network-manager-web.

Configuração e estrutura

O back-end Web do projeto roda em um servidor PHP, mais precisamente, no pacote MAMP, com configurações:

  • Apache 2.2.29;
  • PHP 5.6.2;
  • MySQL 5.5.38.

A estrutura do projeto está como segue:

Estrutura do projeto, lado Web, no PHPStorm

Na lógica de negócio utilizada, os arquivos tracking dentro do diretório /data têm como sufixo o id do usuário da aplicação e dentro de cada um desses arquivos contém as coordenadas de seus respectivos usuários.

Para desenvolver o projeto Web utilizei o PHPStorm, mas qualquer editor de código ou IDE, como o Eclipse, é o bastante.

Domínio do problema

Temos apenas duas classes de domínio do problema na versão Web do projeto. Segue o código de User:

class User
{
private $id;
private $tracking;

public function setByPOST()
{
$this->id = $_POST['user_id'];

$this->tracking = new Tracking();
$this->tracking->setByPOST();
}

public function save()
{
$file = fopen( '../../data/tracking-'.$this->id.'.txt', 'a' );
fwrite( $file, $this->tracking->getLatitude() );
fwrite( $file, Tracking::SEPARATOR );
fwrite( $file, $this->tracking->getLongitude()."\n" );
fclose( $file );
}
}

 

E então o código de Tracking:

class Tracking
{
const SEPARATOR = '__sdsdvsvs__';
const SUCCESS = '0';
const FAILURE = '2';

private $latitude;
private $longitude;


public function setByPOST()
{
$this->latitude = $_POST['latitude'];
$this->longitude = $_POST['longitude'];
}

public function getLatitude()
{
return $this->latitude;
}

public function getLongitude()
{
return $this->longitude;
}
}

 

Ambas são bem simples, adicionadas somente para manter o projeto Web funcional.

Controlador

O controlador do projeto Web é opcional, mas passa a possibilidade de adição de ainda mais lógica de negócio na versão Web. Esse controlador é responsável por direcionar a execução correta de acordo com a entrada de dados pela superglobal $_POST.

Segue código de CtrlUser:

<?php
include_once '../../domain/User.php';
include_once '../../domain/Tracking.php';


if( strcasecmp($_POST['method'], 'user-tracking') == 0 ){

$user = new User();
$user->setByPOST();
$user->save();

echo Tracking::SUCCESS;
exit;
}

echo Tracking::FAILURE;

 

Note que CtrlUser é um arquivo, puro, PHP e não uma classe PHP.

Projeto de exemplo - parte Android

Aqui vamos seguir a configuração da versão Android. Como para a seção anterior, configuração da versão Web, vamos colocar a maior parte do código aqui, digo, o suficiente para você já colocar em um novo projeto no Android Studio.

Mas caso queira o acesso até mesmo aos arquivos de mídia e de configuração de uma projeto no Android Studio, você pode estar acessando o projeto, versão Android, em: https://github.com/viniciusthiengo/gps-tracking-gcm-network-manager.

Para seguir com o exemplo, crie um novo projeto (empty activity) no Android Studio, de preferência com o nome "GPS Tracking GCMNetworkManager".

Configurações Gradle

Abaixo a configuração do Gradle Top Level ou build.gradle (Project: GPSTrackingGCMNetworkManager):

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
}
}
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

 

Note que foi adicionado o repositório maven { url "https://jitpack.io" }, isso devido ao uso da library Simplify-Permissions que vamos utilizar para permissão em tempo de execução. Mais abaixo vamos apresenta-la melhor.

Agora a configuração do Gradle App Level ou build.gradle (Module: app):

apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "br.com.thiengo.gpstrackinggcmnetworkmanager"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.0.1'
testCompile 'junit:junit:4.12'

/* Permission System Library */
compile 'com.github.anshulagarwal06:Simplify-Permissions:v1'

/* LocationManager */
compile 'com.github.zellius:rxlocationmanager:0.1.1'

/* Retrofit */
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

compile 'com.google.android.gms:play-services-gcm:10.0.0' /* GCMNetworkManager */
}

 

Como informado anteriormente, a partir da API 9 já é possível utilizar o GCMNetworkManager. O minSdkVersion 16 é devido a library de permissão em tempo de execução, Simplify-Permissions, que está sendo utilizada.

De qualquer forma, a RxLocationManager utiliza como API mínima a API 14, esse seria nosso "piso" sem a library de permissions.

Configurações AndroidManifest

Abaixo as configurações do AndroidManifest.xml do projeto:

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.thiengo.gpstrackinggcmnetworkmanager">

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity android:name=".MainActivity">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<service
android:name=".service.CustomService"
android:exported="true"
android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">

<intent-filter>
<action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
</intent-filter>
</service>

</application>
</manifest>

 

Note que além da já explicada permissão RECEIVE_BOOT_COMPLETED temos outras quatro. Isso, pois precisamos nesse projeto de acesso a Internet (Retrofit) e de obtenção de coordenadas (RxLocationManager).

Atividade principal (MainActivity)

Aqui vou começar com o layout da activity, mais precisamente o arquivo activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="br.com.thiengo.gpstrackinggcmnetworkmanager.MainActivity">

<TextView
android:layout_marginTop="25dp"
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/colorPrimaryDark"
android:textSize="38sp"
android:text="GCM Network Manager" />

<Button
android:textSize="18sp"
android:id="@+id/bt_tracking"
android:text="Iniciar tracking"
android:layout_centerHorizontal="true"
android:onClick="tracking"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorAccent"/>
</RelativeLayout>

 

Bem simples, direto de uma empty activity. Esse layout vai permitir termos a seguinte tela:

Tela principal do aplicativo Android de exemplo

Ok, já lhe entendi, o background, certo?

Esse é colocado via /values/styles.xml, a linha destacada abaixo:

<resources>
<style name="AppTheme" parent="Theme.AppCompat">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowBackground">@drawable/background</item>
</style>
</resources>

 

Agora, antes de prosseguir com o código da MainActivity, veja o código da classe Util

public class Util {
private static final String SP = "SP";
public static final String TRACKING_STATUS = "tracking_status";

public static void saveSP(
Context context,
String key,
boolean value ){

SharedPreferences sp = context.getSharedPreferences( SP, Context.MODE_PRIVATE );
sp.edit().putBoolean( key, value ).apply();
}

public static boolean retrieveSP(
Context context,
String key ){

SharedPreferences sp = context.getSharedPreferences( SP, Context.MODE_PRIVATE );
return sp.getBoolean( key, false );
}
}

 

Em nosso projeto de exemplo precisamos de alguma maneira saber qual o estado atual da execução do tracking, isso para que não realizemos uma ativação quando esse já está ativado.

Com a classe Util conseguimos encapsular o acesso ao SharedPreferences, uma entidade de persistência simples que vai nos permitir isso, controlar o estado do tracking.

Com isso, de forma parcial, vamos acessar a primeira parte do código da MainActivity:

public class MainActivity extends MarshmallowSupportActivity {

@Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );

changeButtonLabel();
}

private void changeTrackingStatus(){

boolean status = Util.retrieveSP( this, Util.TRACKING_STATUS );
Util.saveSP( this, Util.TRACKING_STATUS, !status );
}

private void changeButtonLabel(){

Button bt = (Button) findViewById( R.id.bt_tracking );

if( Util.retrieveSP( this, Util.TRACKING_STATUS ) ){
bt.setText( "Parar tracking" );
}
else{
bt.setText( "Iniciar tracking" );
}
}

...
}

 

O método changeTrackingStatus() permite que facilmente alteremos o estado atual do tracking do aplicativo.

O método changeButtonLabel() permite que alteremos o rótulo do Button de activity_main.xml de acordo com o estado do tracking do app, isso para o usuário saber quando iniciar e quando parar o tracking.

A herança MarshmallowSupportActivity é devido a library Simplify-Permissions que vamos apresentar na seção logo abaixo.

Configuração da library Simplify-Permissions

Essa library permite que utilizemos de forma simples e segura o script de solicitação de permissão em tempo de execução.

Segue a página dela no GitHub: https://github.com/anshulagarwal06/Simplify-Permissions.

Então a configuração da Simplify-Permissions na MainActivity:

public class MainActivity extends MarshmallowSupportActivity {
...
public void tracking( View view ){

String[] LOCATION_PERMISSIONS = {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
};

Permission.PermissionBuilder permissionBuilder = new Permission
.PermissionBuilder(
LOCATION_PERMISSIONS,
0,
new InternPermissionRequest()
);

requestAppPermissions( permissionBuilder.build() );
}


private class InternPermissionRequest implements Permission.PermissionCallback{

@Override
public void onPermissionGranted( int i ) {
setTrackingTask();
}

@Override
public void onPermissionDenied( int i ) {}

@Override
public void onPermissionAccessRemoved( int i ) {}
}
}

 

O método tracking() está vinculado ao listener de clique do Button do activity_main.xml.

Note que sem uma instância que implementa a interface PermissionCallback, terceiro parâmetro em PermissionBuilder, a library retorna uma exceção.

O segundo parâmetro dePermissionBuilder, 0, seria um requestCode para ser utilizado no onActivityResult() se estivéssemos acessando a Camera2 API, por exemplo.

Na implementação de PermissionCallback, a classe interna InternPermissionRequest, somente o método onPermissionGranted() é útil para nosso exemplo de aplicativo de GPS Tracking.

Isso, pois é apenas um exemplo. Para projetos que vão para produção, você trabalharia todos os métodos para dar um feedback válido ao usuário.

Se você está se perguntando o porquê de a classe interna não ser estática.

Saiba que essa classe nunca terá o ciclo de vida maior que a classe pai dela, a MainActivity, logo, não temos a necessidade de remover a referência implícita que há para a MainActivity, quando a classe interna não é static.

O método setTrackingTask() é referente as configurações do GCMNetworkManager. Esse método é explicado logo abaixo.

Configuração do GCMNetworkManager

Começando pelas configurações de criação de tarefa:

public class MainActivity extends MarshmallowSupportActivity {
...
private void setTrackingTask(){

GcmNetworkManager gnm = GcmNetworkManager.getInstance( this );
changeTrackingStatus();
changeButtonLabel();

if( !Util.retrieveSP( this, Util.TRACKING_STATUS ) ){
gnm.cancelAllTasks( CustomService.class );
return;
}

PeriodicTask task = new PeriodicTask.Builder()
.setTag( "periodic" )
.setService( CustomService.class )
.setPeriod( 20 )
.setFlex( 10 )
.setRequiredNetwork( Task.NETWORK_STATE_UNMETERED )
.setRequiresCharging( false )
.setUpdateCurrent( true )
.setPersisted( true )
.build();

gnm.schedule( task );
}
...
}

 

Note que não tivemos a necessidade de termos o GcmNetworkManager como variável de instância, ele é utilizado somente no método setTrackingTask().

No único condicional do método verificamos se o tracking já está em execução, caso sim, alteramos o label do Button, cancelamos todas as tarefas (sempre será somente uma) e saímos do método sem criar uma nova task.

Agora a parte da configuração do Service CustomService que herda de GcmTaskService:

public class CustomService extends GcmTaskService {
private static final String TAG = "log";
private Location location;

@Override
public int onRunTask( TaskParams taskParams ) {

retrieveCoordinate();
lockThreadUntilLocationNotNull();
sendCoordinate();

return GcmNetworkManager.RESULT_SUCCESS;
}
...
}

 

Nas configurações que serão apresentadas nas seções a seguir você entenderá o porquê dos métodos retrieveCoordinate(), lockThreadUntilLocationNotNull() e sendCoordinate(), além do entendimento da existência da variável de instância location.

Note que depois do processamento dos métodos dentro de onRunTask() sempre estaremos retornando que a execução foi realizada com sucesso.

Em nossa lógica de negócio não há problemas com isso, mas em seu aplicativo você deve avaliar para retornar a opção correta (SUCCESS, FAILURE ou RESCHEDULE).

Configuração da library RxLocationManager

Essa library nos permite acesso simples a coordenadas disponíveis no device, caso não haja uma, podemos facilmente configurar qual será o provider que solicitará uma nova coordenada.

Para acessar a página dessa library no GitHub entre em: https://github.com/Zellius/RxLocationManager.

Utilizamos essa library em CustomService, segue:

public class CustomService extends GcmTaskService {

private static final String TAG = "log";
private Location location;

@Override
public int onRunTask( TaskParams taskParams ) {

retrieveCoordinate();
lockThreadUntilLocationNotNull();
sendCoordinate();

return GcmNetworkManager.RESULT_SUCCESS;
}

private void retrieveCoordinate(){
LocationRequestBuilder locationRequestBuilder = new LocationRequestBuilder(
getApplicationContext()
);

locationRequestBuilder
.addLastLocation(
LocationManager.NETWORK_PROVIDER,
new LocationTime( 30, TimeUnit.SECONDS ),
false
)
.addRequestLocation(
LocationManager.GPS_PROVIDER,
new LocationTime( 10, TimeUnit.SECONDS )
)
.setDefaultLocation(
new Location( LocationManager.PASSIVE_PROVIDER )
)
.create()
.subscribe( new Subscriber<Location>() {

@Override
public void onCompleted(){}

@Override
public void onError( Throwable e ){}

@Override
public void onNext( Location l ) {
location = l;
}
});
}

private void lockThreadUntilLocationNotNull(){

while( location == null ){
SystemClock.sleep( 1000 );
}
}
...
}

 

Depois de criarmos a instância de LocationRequestBuilder trabalhamos a configuração de como será feita a busca de coordenada no device.

O método addLastLocation() aceita, respectivamente, os dados de configuração: provider utilizado para buscar a uma coordenada; tempo máximo de vida aceito para a coordenada buscada e; se o valor null pode ser retornado, se é válido para a lógica de negócio trabalhada (em nosso exemplo, não).

O método addRequestLocation() aceita como configuração: o provider que será utilizado para a busca de uma nova coordenada e; o tempo limite de espera por essa nova coordenada, o timeout.

Em caso de falha nos dois métodos anteriores, o método setDefaultLocation() retorna uma coordenada padrão do device.

O método create() ativa a instância para buscar a coordenada e o método subscriber() trabalha como a parte subscriber do padrão de projeto Observer, inscreve uma entidade para que possa ouvir quando houver alguma coordenada disponível.

Em nosso caso criamos uma instância anônima de Subscriber<Location>. Utilizamos o método onNext() para obtermos as coordenadas e então coloca-las na variável de instância location.

Ok, mas por que essa lógica de negócio "estranha"?

Isso, pois a execução de LocationRequestBuilder é assíncrona, mesmo sabendo que ela iniciou o trabalho em uma Thread de background.

Juntando que o retorno em onNext() acontece na Thread principal. Porém, em nosso projeto, esses dados, digo, as coordenadas, terão ainda de ser enviados via conexão com a Internet, fora da Thread principal.

Logo, obtendo a referência no onNext() e então prosseguindo com a execução em onRunTask() nós ainda continuamos na Thread de background.

A, então é por isso que você colocou aquela "gambiarra" no método lockThreadUntilLocationNotNull()?

Sim, com aquele "loop de trava" (sinônimo para gambiarra), nós podemos aguardar a resposta do LocationRequestBuilder sem terminar o processamento, mesmo sendo assíncrona a execução de LocationRequestBuilder.

Lembrando que não precisamos nos preocupar com o tempo de execução desse loop. Mesmo que dê algum problema e onNext() nunca seja invocado, a aplicação continuará em execução por no máximo três minutos.

Configuração do Retrofit

Se já acompanha o Blog e Canal a algum tempo o Retrofit não será figura nova para ti, já o estou utilizando a várias aulas aqui.

Caso tenha alguma dúvida sobre como ele funciona, veja o artigo que tenho sobre ele aqui no Blog: Library Retrofit 2 no Android.

Com isso vamos começar com a definição da Interface que servirá para permitir a comunicação do Retrofit com nossa API Web. Segue código de UserTracking:

public interface UserTracking {

@FormUrlEncoded
@POST( "package/ctrl/CtrlUser.php" )
public Call<String> sendCoordinates(
@Field("method") String method,
@Field("user_id") String id,
@Field("latitude") String value,
@Field("longitude") String token
);
}

 

E então o código do método sendCoordinate() em CustomService:

public class CustomService extends GcmTaskService {
...
private void sendCoordinate(){

Retrofit retrofit = new Retrofit.Builder()
.baseUrl( "http://192.168.25.221:8888/gps-tracking-gcm-network-manager/" )
.addConverterFactory( GsonConverterFactory.create() )
.build();

UserTracking userTracking = retrofit.create( UserTracking.class );

Call<String> request = userTracking.sendCoordinates(
"user-tracking",
"564s6d54s6d",
String.valueOf( location.getLatitude() ),
String.valueOf( location.getLongitude() )
);

String answer = null;
try{
answer = request.execute().body();
}
catch( Exception e ){}

Log.i(TAG, "-----> "+answer);
}
}

 

Nada de novo do que já explicamos aqui no blog sobre o Retrofit. Somente note que por já estarmos dentro de uma Thread de background, nós optamos por utilizar o Retrofit de modo síncrono, sem callback.

Como forma de debug, apenas vamos printar nos logs do Android Studio o retorno da API Web.

Testes e resultados

Crie um emulador que tenha as Google APIs (ou execute o exemplo em um device real). Logo em seguida rode o emulador e então instale o aplicativo nele. Clique no botão "Iniciar Tracking" e terá como resultado algo como:

Tela principal do app Android de exemplo em teste

Para simular coordenadas no emulador, clique em "..." na barra direita de botões que aparece ao lado dele. Então clique em "Location", coloque as coordenadas e clique em "send":

Simulando coordenadas no emulador Android

Pode seguramente sair do aplicativo, até mesmo removê-lo do background.

Com isso, passados alguns segundos (de acordo com o definido em setPeriod() e setFlex()) temos os seguintes resultados no back-end Web:

Acessando no back-end Web o arquivo com as coordenadas gravadas

Assim finalizamos a configuração de nosso simples aplicativo Android GPS Tracking.

Antes de prosseguir, não esqueça de se inscrever na 📫 lista de e-mails do Blog para receber, em primeira mão, os conteúdos Android de desenvolvimento.

Se inscreva também no canal do Blog em YouTube Thiengo.

Vídeo com implementação passo a passo do exemplo

No vídeo a seguir é apresentado o passo a passo da implementação do aplicativo de tracking do artigo:

Para acessar a versão Android do Projeto, entre no GitHub a seguir: https://github.com/viniciusthiengo/gps-tracking-gcm-network-manager

Para acessar a versão Web, entre no seguinte GitHub: https://github.com/viniciusthiengo/gps-tracking-gcm-network-manager-web

Conclusão

Sem sombra de dúvidas que há muitos benefícios no uso do GCMNetworkManager, mas infelizmente não temos a opção de controla-lo para a exata execução, digo, no tempo determinado.

Com isso devemos primeiro estudar as opções que temos para execução de tarefas em background e então, baseando-se nas informações coletadas, escolher a melhor opção para o projeto Android.

Na seção AlarmManager x JobScheduler x GCMNetworkManager x SyncAdapter discutimos isso bem. Porém os links de Fontes vão lhe ajudar nas dúvidas caso ainda existam.

Não esqueça do Doze Mode, o que você pode achar como sendo um bug, na verdade é uma característica do Android para economia de bateria.

O Evernote (você muito provavelmente conhece esse software) tem uma library que facilita ainda mais o trabalho com essas entidades de execução de tarefa em background. Acesse ela em: https://github.com/evernote/android-job.

Curtiu o conteúdo? Não esqueça de compartilha-lo. E, por fim, de se inscrever na 📩 lista de e-mails, respondo às suas dúvidas também por lá.

Abraço.

Fontes

Implementing GCM Network Manager on Android

Doc reference - GcmNetworkManager

Doc reference - OneoffTask.Builder

Doc reference - PeriodicTask.Builder

Doc reference - GcmTaskService

GcmNetworkManager example - Veja as FAQ desse link

Optimize Battery Life with Android's GCM Network Manager

Choosing the Right Background Scheduler in Android

Diving into Doze Mode for Developers

How Google Cloud Messaging handles Doze in Android 6.0 Marshmallow

Evernote Android Job Library

Simplify-Permissions Library - Utilizada no exemplo do artigo

RxLocationManager Library - Utilizada no exemplo do artigo

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

ConstraintLayout, Melhor Performance no AndroidConstraintLayout, Melhor Performance no AndroidAndroid
Checkout Transparente da Web no AndroidCheckout Transparente da Web no AndroidAndroid
Proguard AndroidProguard AndroidAndroid
Input File no WebView AndroidInput File no WebView AndroidAndroid

Compartilhar

Comentários Facebook

Comentários Blog (19)

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...
23/08/2018
Qual forma seria mais indicado para criar um agendamento que periodicamente o aplicativo vai pegar dados de uma webView e jogar no SQLite, lembrando que o sistema não tem um servidor.
Responder
Vinícius Thiengo (1) (0)
24/08/2018
Joel, tudo bem?

Como você deve ter visto em parte do artigo acima, desde o Android 6, Marshmallow, há algumas novas regras de negócio no Android que fazem com que o agendamento exato (execução em horário exato, sempre) de tarefas em background seja dificultado.

De qualquer forma, se a exatidão não for uma necessidade em seu domínio de problema, o próprio GcmNetworkManager é uma boa opção para o agendamento que você está precisando em seu projeto.

Mas hoje eu recomendaria mais o trabalho com o WorkManager, uma API mais atual e nativa do Android que tem a mesma proposta que o GcmNetworkManager, porém com um processamento mais eficiente:

https://developer.android.com/topic/libraries/architecture/workmanager/

Caso a exatidão seja necessária, então recomendo que estude por completo o conteúdo do link a a seguir:

https://developer.android.com/topic/performance/scheduling

Abraço.
Responder
24/08/2018
O WorkManager está em alfa ainda, e parece que exige API 28+. Acho que vou ficar com o GCMNetworkManager pelo jeito. :/
De qualquer forma, muito obrigado Thiengo
Responder
Carlos Zudoski (1) (0)
30/03/2018
Muito o artigo. Não conhecia esse método.

Agora eu resolvi meu problema com o AlarmManager e o WakeLock.  De 5 em 5 meu app envia as localizações.
Responder
28/11/2017
Saudações Thiengo. Top demais seu tutorial cara. Segui o passo a passo aqui e deu certinho.. Uma dúvida. Será que esse tipo de abordagem funcionará também para o Android Oreo? Vi que ele virá cheio de restrições as tarefas de background que utlizam conexões externas...  Uma outra dúvida. Tive problemas para fazer esse app funcionar num S7 edge com android 7. Porem num emulador com a mesma versão do android e num K10 com android 6 funcionaram de boa. Será que existe alguma particularidade nos celularem da samsung que impedem o funcionamento do GCM? No mais, parabens pelo canal e sucesso!
Responder
Vinícius Thiengo (0) (0)
30/11/2017
David, tudo bem?

Sobre a primeira dúvida: provavelmente o funcionamento do GCMNetworkManager persistirá, mas pode ser que com ainda mais limitações, por exemplo, com o AlarmManager, que hoje ainda conseguimos a execução dele de 15 em 15 minutos ao menos, mesmo com o Modo Soneca.

Esse intervalo pode ser aumentado. A notificação push de alta prioridade pode não mais ter prioridade ante a remover o aplicativo do modo soneca, entre outros.

Sobre o funcionamento no Samsung, temos sim de ter em mente que a modificação no SO Android para os devices dessa marca, mesmo que somente de um modelo em específico, afeta o funcionamento da APIs utilizadas nos aplicativos.

Ou seja, este pode estar sendo o problema / limitação no S7 Edge.

Um outro exemplo é o Zenphone da Asus. Caso o usuário não autorize, nas configurações do device, o recebimento de notificações push quando o aplicativo não está em background e nem em foreground, se não houver essa autorização e o app não é um muito conhecido (é sério!) a notificação não é entregue.

David, obrigado pelo apoio ao Blog e canal.

Abraço.
Responder
Robson (1) (0)
31/10/2017
Olá Thiengo boa tarde, cara show este seu tutorial e eu mais uma vez aqui pra perguntar, já tinha visto e feito uso de GcmNetworkManager e antes eu estava usando retrofit agora não, diferente de vc não estou usando retrofit mais a lib com.loopj.android.http.AsyncHttpClient e lógico sera por ser uma chamada assincrona dentro do onRunTask que estou recebendo este erro?
Synchronous ResponseHandler used in AsyncHttpClient. You should create your response handler in a looper thread or use SyncHttpClient instead
Responder
Vinícius Thiengo (1) (0)
08/11/2017
Robson, tudo bem?

Tentou utilizando a API SyncHttpClient ao invés de AsyncHttpClient como indicado em mensagem?

A princípio esta é a solução.

Abraço.
Responder
Robson (1) (0)
08/11/2017
Olá meu caro boa tarde, obrigado pelo retorno e sim estou usando o SyncHttpClient com ele eu conseguir sair daquele erro mais então não tem jeito de usar assincrono?
Responder
Vinícius Thiengo (0) (0)
14/11/2017
Robson, excelente que você conseguiu resolver o problema.

Sobre a pergunta, é aquilo, se seguirmos ao pé da letra a mensagem de erro: não é possível.

Mas sempre há uma maneira de mudar o contexto, Thread, e então utilizar as APIs desejadas. Em seu caso as APIs de execução assíncrona.

Mas avalie bem se vale a pena se dedicar a encontrar esse novo contexto ao passo que você já tem um algoritmo funcional utilizando a API SyncHttpClient.

Abraço.
Responder
Henrique França (1) (0)
12/07/2017
Olá excelente tutorial, me surgiu uma duvida aqui, eu preciso fazer uma validação na qual preciso realizar algumas coisas após minha task ser cancelada, porém estou com problemas para achar algo que me indique que minha task foi realmente cancelada pelo comando cancelAllTasks / cancelTask
Responder
Vinícius Thiengo (1) (0)
13/07/2017
Henrique, tudo bem?

Sabendo que esses métodos deverão ser invocados em código, digo, os métodos cancelAllTasks() / cancelTask().

Uma maneira simples de resolver isso é utilizando uma variável classe (campo estático) do tipo boolean em alguma classe que está fortemente ligada a lógica do GcmNetworkManager.

Assim, quando houver a invocação de cancelAllTasks() ou cancelTask(), marque essa variável de classe como true. Ao fim da execução de algoritmo que verificou a variável de classe como true (e assim pode executar) marque a variável de classe novamente como false.

Estará trabalhando com uma flag, mesmo assim essa não deixa de ser uma solução simples. Abraço.
Responder
Henrique França (1) (0)
24/07/2017
Show de bola Thiengo, segui esta linha de raciocínio e utilizei o booleano mesmo, me parece que o cancelAllTasks() cancela mas não de forma definitiva e o Android da o start novamente do serviço, eu ajustei minha lógica conforme vc comentou e ficou certinho, o código dentro do onRunTask() só está sendo executado de acordo com a flag. Show valeu pela ajuda.
Responder
02/06/2017
Uma dúvida, para buscar dados de um servidor web de hora em hora por exemplo, o GCM também é recomendado?
Isso seria definido no PeriodicTask.setPeriod?
Obrigado
Responder
Vinícius Thiengo (0) (0)
06/06/2017
José, tudo bem?

Hoje, de acordo com as características das APIs de time request do Android, o recomendado é que o algoritmo não precise de ser tão preciso, digo, exatamente de hora em hora.

Neste caso o ideal é que você ajuste a lógica de seu projeto para poder trabalhar sem problemas com a requisição podendo ocorrer com alguns intervalos maiores do que uma hora e em algumas vezes até menores.

Aceitando essa regra de negocio do Android, eu recomendo o uso do GcmNetworkManager, principalmente porque ele vai lhe alivia-lo de ter de construir código extra para versão do Android que não suportam o JobScheduler, por exemplo.

Uma outra opção é utilizar o AlarmManager para acordar o aplicativo de uma em uma hora. Porém já tive feedbacks de que essa regra também não funciona para o AlarmManager, ele terá alguns delays como o GcmNetworkManager. Além disso com o AlarmManager você também terá de utilizar um Service e um BroadcastReceiver.

Isso, pois o AlarmManager acionará um Service que permitirá uma requisição de tempo indeterminado ao seu backend. O BroadcastReceiver será utilizado para restaurar o AlarmManager caso o device seja rebootado.

Se não for possível atualizar as regras de negocio de seu aplicativo para não precisar de exatidão nas requisições de hora em hora, veja se utilizando o algoritmo de solicitação de colocação de app na white-list funciona para ti, digo, utilizando ele junto a um algoritmo de AlarmManager, mas agora sem o efeito Doze Mode (modo soneca).

Mais sobre esse algoritmo de white-list no link a seguir: https://stackoverflow.com/a/32627788/2578331

Caso queira saber mais sobre o AlarmManager, Service e BroadcastReceiver, veja os links a seguir:

http://www.thiengo.com.br/alarmmanager-no-android-sua-app-executando-em-tempos-definidos
http://www.thiengo.com.br/service-android-entendendo-e-utilizando
http://www.thiengo.com.br/broadcastreceiver-no-android-executando-tarefas-no-background

Sobre a opção de utilizar notificação com prioridade alta, segundo meus testes isso não funciona, mas nãodeixa de ser uma possibilidade de você incluir o algoritmo de requisição em white-list.

Mais sobre notificação FCM no link a seguir: http://www.thiengo.com.br/fcm-android-dominio-do-problema-implementacao-e-testes-com-servidor-de-aplicativo-parte-1

Abraço.
Responder
Ruan Alves (1) (0)
05/04/2017
Opa, Blz? A um tempo estou utilizando essa opção, pra min resolveu os meus problemas com facilidade, so que surgiu uma breve dúvida: supondo que eu necessite em 3 em 3 minutos pegar a localização do usuário, irei usar o GCM, so que no caso como você mesmo disse ele tem um tempo limite, as vezes para pegar a localização demora, pois usa o GPS sem rede, no caso tem como colocar um delimitador de tempo, donde se ultrapassar o tempo proposto ele cancela a operação? .... vlw ...
Responder
Vinícius Thiengo (0) (0)
08/04/2017
Ruan, tudo bem?

O delimitador de tempo que vejo seria uma Thread secundária aberta com um código de cronometro, SystemClock.slee(), onde quando atingido o tempo de 3 minutos, seria verificado se já há uma nova coordenada, caso não, desativa a conexão do listener de coordenadas.

Pode até mesmo utilizar uma flag para apontar que se a coordenada for entregue ela deve ser descartada. Uma flag que terá o valor alterado no algoritmo de cronometro.

Veja se consegue atualizar as regras de negócios de seu sistema para que mesmo extrapolando os 3 minutos seja possível aceitar as coordenadas retornadas. Para mim essa é a melhor opção. Abraço.
Responder
Ruan Alves (1) (0)
08/04/2017
Opa blz ? ... Entendi, só que no meu caso por exemplo as vezes as coordenadas vem em atraso, mas vem, na verdade não sei o por que a demora .... por isso pensei na ideia do limitador de tempo ...
Responder
Ruan Alves (1) (0)
30/11/2016
Esse método de escrever um artigo detalhado e depois o vídeo, é simplesmente sensacional, no caso já vamos pro vídeo (se precisar) com muita coisa engatinhada ... Showww
Responder