➙ Conteúdo completo, texto e vídeo, em: https://www.thiengo.com.br/true-time-api-para-data-e-horario-ntp-no-android
Neste conjunto de slides vamos ao estudo da importância de se ter data e horário exatos em alguns domínios de problema Android.
Também aprenderemos como obter essa exatidão utilizando uma biblioteca específica para comunicação com servidores NTP, mais precisamente a biblioteca True Time Android.
➙ Para receber o conteúdo do blog em primeira mão, assine a lista de emails em: http://www.thiengo.com.br
Abraço.
▶ Treinamento oficial:
➙ Prototipagem Profissional de Aplicativos Android:
↳ https://www.udemy.com/android-prototipagem-profissional-de-aplicativos/?couponCode=TRUE_TIME_API&persist_locale&locale=pt_BR
▶ Livros oficiais:
➙ Desenvolvedor Kotlin Android - Bibliotecas para o dia a dia:
↳ https://www.thiengo.com.br/livro-desenvolvedor-kotlin-android
➙ Receitas Para Desenvolvedores Android:
↳ https://www.thiengo.com.br/livro-receitas-para-desenvolvedores-android
➙ Refatorando Para Programas Limpos:
↳ https://www.thiengo.com.br/livro-refatorando-para-programas-limpos
▶ Redes:
➙ Udemy: https://www.udemy.com/user/vinicius-thiengo/?persist_locale&locale=pt_BR
➙ YouTube: https://www.youtube.com/user/thiengoCalopsita
➙ Facebook: https://www.facebook.com/thiengoCalopsita
➙ LinkedIn: https://www.linkedin.com/in/vin%C3%ADcius-thiengo-5179b180/
➙ GitHub: https://github.com/viniciusthiengo
➙ Twitter: https://twitter.com/thiengoCalops
➙ Google Plus: https://plus.google.com/+ThiengoCalopsita
▶ Blog App:
➙ https://play.google.com/store/apps/details?id=br.thiengocalopsita&hl=pt_BR
2. Network Time Protocol - NTP
O Network Time Protocol (NTP), ou Protocolo de Tempo para Redes, é o protocolo que
permite a sincronização dos relógios de dispositivos que se conectam a Internet.
O NTP é extremamente importante para qualquer dispositivo que se conecta a rede, um
forte indício disso é que qualquer aparelho computacional que adquirimos já vem com a
configuração de obtenção de data e hora em: atualização automática.
A discussão detalhada sobre o funcionamento do NTP você consegue no site oficial NTP.br
mantido pelo Comitê Gestor da Internet (CGI).
Estou indicando o CGI para mais informações, pois o estudo por completo do NTP tende a
não ser de grande importância a um profissional de desenvolvimento, principalmente
porque o dispositivo que receberá o software desenvolvido certamente já tem nele toda a
tecnologia de requisição de data e horário a servidores NTP.
O que é importante ao profissional de desenvolvimento de software é saber se o domínio
do problema atualmente trabalhado por ele exige, ou não, o gerenciamento de data e hora
exatos, independente da data e horário fornecidos pelo próprio aparelho.
Isso, pois o aparelho não é uma entidade confiável em termos de "data e hora exatos”,
principalmente porque o usuário pode mudar estes dados para atender a alguma
necessidade dele, por exemplo: adiantar o horário em dez minutos para sempre conseguir
sair ao menos dez minutos antes aos compromissos.
3. Simple Network Time Protocol - SNTP
O Simple Network Time Protocol (SNTP), ou Protocolo Simples de Tempo para
Rede, é uma versão simplificada do NTP, versão que não exige, por exemplo, a
complexidade de obter informações de tempo de um determinado servidor,
informações como: deslocamento, dispersão e variação.
Resumidamente, o SNTP permite: consultar o tempo em um servidor e ajustar o
relógio local de tempos em tempos. O NTP vai mais além, como, por exemplo:
obter os dados de tempo (data e hora) da fonte mais confiável dentre todas as
pesquisadas.
Para ficar mais claro: se em seu domínio é necessária uma requisição a um
servidor qualquer para então obter a data e horário desse servidor como fonte de
atualização de relógio, essa foi uma atualização utilizando o protocolo SNTP.
Agora se em seu domínio do problema houve a requisição a uma série de
servidores de data e hora, incluindo o trabalho de escolha da melhor resposta. Isso
já se caracteriza como fluxo de uso do protocolo NTP.
A biblioteca que estudaremos neste conjunto de slides trabalha com a versão
completa do NTP, mesmo ela exigindo poucas linhas de código.
4. Quando o NTP é importante para um desenvolvedor
Android
Certamente você deve estar se perguntando: Para desenvolvedores Android, qual a
utilidade do conhecimento de alguma API NTP?
A importância deste conhecimento fica evidente principalmente quando trabalhando
em domínios de problema onde há necessidade de comunicação remota com
servidores Web e também à exigência de trabalho offline, o app continua
funcionando (de maneira limitada) mesmo quando não há conexão com a rede.
Aplicativos de comércio eletrônico, por exemplo, exigem inúmeros dados que serão
utilizados na aplicação de inteligência em negócios, para assim decisões acertadas
serem tomadas, decisões de promoções em determinadas regiões, por exemplo.
Conhecer o tempo exato da maioria das interações dos usuários de seu aplicativo
de compras online provavelmente trará maior precisão nas tomadas de decisões.
Note que o conjunto de domínios de problemas que exigem exatidão em data e
horário, uso de API NTP, não é tão extenso quanto o conjunto de domínios que
exigem o trabalho com alguma API de carregamento de imagens remotas, mas
certamente os domínios para NTP também existem em grande número e até o
momento da construção destes slides nós, desenvolvedores Android, temos
somente uma API open source que permite requisição a servidores NTP.
6. Biblioteca (ou API) True Time
A biblioteca True Time Android foi desenvolvida para o simples fornecimento de
uma data e horário confiáveis, obtidos de servidores NTP.
O projeto é open source e tem muita aceitação da comunidade de desenvolvedores
Android. Até o momento da construção deste conteúdo eram mais de 690 estrelas
de recomendação no GitHub, algo surpreendente para uma API específica de
domínio.
A True Time API atende a partir do Android API 14, Ice Cream Sandwich, e apesar
de algumas limitações encontradas, se o desenvolvedor Android souber trabalhar
com entidades como AsyncTask e LocalBroadcastManager, certamente ele vai
conseguir o máximo possível da True Time.
7. Instalação da API
A biblioteca está disponível no Jitpack, este que é um repositório de pacotes de
projetos JVM e Android. Logo, no Gradle Project Level, adicione a referência a este
repositório como a seguir (bloco maven):
Então, no Gradle App Level, adicione:
8. No código do anterior, do Gradle App Level, estamos referenciando a versão Rx
(ReactiveX) da True Time API.
Na documentação há também a versão vanilla, versão que não exige o
conhecimento da sintaxe do RxJava quando trabalhando com a True Time.
Porém na mesma documentação ainda é recomendado o uso da versão Rx, pois
ela é a versão que implementa a requisição NTP completa e não somente
requisição SNTP, como acontece com a versão vanilla.
Devido a isso, aqui estudaremos somente a versão Rx. Fique tranquilo se você
ainda não conhece o RxJava, a interface da True Time API é simples de entender e
de utilizar, ou seja, este "não conhecimento" não lhe atrapalhará.
9. Configuração inicial para requisição a servidores NTP
O código de requisição da True Time API deve entrar no método onCreate() de
uma classe Application. Veja o código a seguir:
10. Comentando o código anterior, temos:
- build(): retorna um objeto TrueTimeRx. Note que build() invoca é a
implementação do padrão Singleton, ou seja, sempre estaremos utilizando o
mesmo objeto TrueTimeRx;
- withSharedPreferences( this ): indica que o resultado retornado, de algum
dos servidores NTP, deve ser colocado em cache, cache gerenciado pela
própria biblioteca e utilizando a API SharedPreferences. O contexto,
argumento, sempre é o da aplicação;
- initializeRx( "time.apple.com" ): definição do conjunto, pool de servidores,
NTP para a obtenção da data e horário confiáveis;
- subscribeOn( Schedulers.io() ): método que permite que nós
desenvolvedores especifiquemos em qual operador de comunicação e thread a
entidade observável trabalhará. Aqui a entidade observável é o "algoritmo de
requisição a servidores NTP", onde o resultado dessa requisição deve ser
enviado a todos os observadores (inscritos via método subscribe(), por
exemplo). Segundo a documentação Rx da True Time API, o operador deve ser
o Schedulers.io().
11. - subscribe(…): assina uma entidade observadora para resultado positivo (sem
erro em tempo de execução), primeiro argumento. E assina uma entidade
observadora para resultado negativo, com erro em tempo de execução,
segundo argumento. Qualquer resultado é transmitido via operador definido em
subscribeOn(). A origem do resultado é uma entidade observável, aqui o
"algoritmo de comunicação com servidores NTP". Há sobrecargas de
subscribe(), mas a versão apresentada anteriormente tende a ser a utilizada
em todos os domínios da True Time API. Fizemos uso da versão Lambda, ou
literal de função, de implementações de Consumer, como na documentação
oficial da True Time.
Se o método withSharedPreferences() não for invocado com o argumento correto,
não será possível reaproveitar a data e horário já retornados por um servidor NTP.
O dado em cache, devido ao uso de withSharedPreferences(), permanecerá em
disco até o momento que houver o reboot do aparelho. Após o reboot, assim que o
aplicativo for aberto será necessária uma nova conexão NTP para obter novamente
a data e horário corretos.
12. Apesar das explicações sobre os métodos subscribeOn() e subscribe(), pode ser
que você queira se aprofundar ainda mais, logo, ao terminar este conteúdo, entre
nos links a seguir:
- Explicação de subscribeOn() no site ReactiveX (em inglês);
- Explicação de subscribe() no site ReactiveX (em inglês).
Pode lhe estar surgindo a dúvida sobre a necessidade de uso dos métodos
subscribeOn() e subscribe() somente para uma simples requisição a servidores
NTP. Segundo meus testes, se não houver a definição destes métodos, não haverá
requisição.
13. Agora, para que a requisição do código TrueTimeRx funcione, ainda é preciso
algumas configurações no AndroidManifest.xml:
14. Obtendo o objeto Date
Além do uso do primeiro argumento de subscribe() para obter o objeto do tipo
Date, objeto que contém data e horário corretos de servidores NTP, também
podemos utilizar o método now() em qualquer trecho de nosso projeto:
É importante o uso de um bloco try{} na invocação de now(), pois caso ainda não
haja uma resposta válida de TrueTimeRx, mesmo que em cache, uma exceção
será gerada.
Ok, mas não há algum método que nos auxilie em saber se já há ou não algum
dado em memória que possibilite a segura invocação de now()?
Até o momento da construção deste conjunto de slides não havia algum método na
API que permitisse essa verificação de cache.
15. Formatando a saída e o fuso horário
A data e horário retornados vão estar dentro do fuso horário do aparelho, mas há
inúmeras APIs Java e Kotlin que permitem a mudança disso em tempo de
execução.
O código a seguir é um exemplo de como obter a data e horário formatados e em
diferente fuso horário:
Para uma lista completa de Greenwich Mean Time (GMT), entre em GeoIPs
TimeZones.
16. Evitando novas invocações de TrueTimeRx.build()
Pode ser que você escolha não realizar mais invocações a TrueTimeRx.build()
depois de já ter no aparelho uma data e horário retornados por servidores NTP.
Para isso, ao invés do uso de isInitialized() que somente verifica se
TrueTimeRx.build() já foi iniciado, utilize um código try{}catch{} como a seguir:
17. Estude bem o domínio do problema do aplicativo em desenvolvimento para então
poder definir com total certeza se o código de segurança anterior é necessário.
Digo isso, pois depois que a data e horário são retornados de algum servidor NTP,
a API True Time utiliza o clocktick do aparelho para poder correr também o relógio
dos dados em cache, data e horário.
Devido a "não exata" precisão do clocktick do aparelho, depois de um tempo o
relógio ou fica adiantado, ou fica atrasado.
Tendo em mente o que foi discutido nos parágrafos anteriores, se seu aplicativo
continuar fazendo uso da data e horário armazenados pela True Time API e
também tiver com o código de bloqueio de novas invocações de
TrueTimeRx.build(), com um tempo a sua lógica de negócio estará trabalhando
com a data e hora errados.
Na documentação não há código de bloqueio. Alias, nos códigos de exemplo,
assim que o aplicativo é aberto, há novas requisições TrueTimeRx.build().
Com isso, o máximo que recomendo é que: se o código de bloqueio for necessário,
então trabalhe com um timer interno para que ao menos uma nova requisição seja
realizada a cada semana.
18. Uma possível limitação em withSharedPreferences()
Quando você for realizar seus testes de código de bloqueio, da seção anterior,
notará que o código em catch() somente não é invocado quando o aplicativo é
aberto em um estado onde ele já estava em background.
Segundo um dos mantenedores da biblioteca True Time, até o momento da
construção deste conjunto de slides: isso é problema de versão de Android /
aparelho em uso, pois nos testes dele o método withSharedPreferences()
funciona sem problemas.
Bom... agora é aguardar a evolução da API para que withSharedPreferences()
funcione para qualquer uma das versões do Android e aparelhos a partir da versão
mínima de API suportada pela True Time.
Tenha em mente que esta limitação não é muito crítica, pois novas requisições a
servidores NTP são sim recomendadas.
19. Se a apresentação de alguma data e horário for realmente necessária, então você
pode tentar algo como:
No projeto de exemplo do artigo que acompanha este conjunto de slides
utilizaremos um código de segurança como o anterior, além de APIs como
AsyncTask e LocalBroadcastManager, para obter o melhor da biblioteca True
Time.
20. Thread Principal (UI) bloqueada
Dependendo da versão da True Time API que você estiver utilizando, assim que
invocar o método TrueTimeRx.now() notará que a interface do usuário vai travar.
Isso acontece até mesmo quando a invocação de now() ocorre em uma thread
secundária.
A versão mais atual da API apresenta este problema. Na época da construção
deste conteúdo a versão mais atual era a 3.3:
21. A solução, encontrada nas issues da API no GitHub, é utilizar a versão
09087b6a6e, versão que a principio nos atende tão bem quanto as versões
posteriores a ela, porém com o adendo de não ter o travamento da thread principal:
Agora é acompanhar as novas versões da API para ver se o problema de trava de
thread UI é resolvido definitivamente.
22. Configurações extras
Ainda é possível o trabalho com outros métodos de RxJava, além de métodos de
configuração nativos da API. Veja o código a seguir:
23. Sendo um conhecedor de Rx, você também consegue utilizar os métodos
observeOn() e subscribeWith(), como no exemplo a seguir, direto da classe
Application, customizada e em Java, da documentação oficial:
24. Servidores NTP
Segundo a documentação oficial da API em estudo e também segundo meus
testes, o melhor pool de servidores NTP a consultar é o da Apple: time.apple.com
Na doc também há alguns exemplos com o pool de servidores NTP do Google:
time.google.com
Você pode tentar definir algum outro de seu conhecimento. Na documentação não
há restrições quanto ao uso de outros servidores, ao menos de maneira explicita
não há essa restrição.
Em meus testes eu não testei a precisão de cada pool de servidores NTP, mas
notei que o da Apple é o mais eficiente em termos de "devolver uma resposta",
rapidamente há o retorno da data e horário requisitados.
25. Pontos negativos
- A documentação, incluindo os códigos de exemplo, não condiz 100% com o
que há disponível de interface pública na biblioteca True Time. O método
withSharedPreferencesCache() de um dos códigos de exemplo, quando
invocado em código, mesmo com a versão 3.3 da API, não é reconhecido;
- O problema de trava de thread principal ainda não foi resolvido na versão
mais atual da API, 3.3;
- O método withSharedPreferences() não tem efeito algum se o aplicativo for
removido da memória primária, pilha de apps em background;
- Não há um simples método de verificação para saber se há ou não alguma
data e horário já obtidos de uma fonte NTP e salvos em disco;
- A versão vanilla da API ainda é apresentada na documentação mesmo
quando somente a versão Rx vem sendo evoluída.
26. Pontos positivos
- Apesar dos problemas, a sintaxe de uso é simples e realmente uma data e
horário exatos são obtidos;
- Os mantenedores da API respondem as issues, facilitando o encontro de
soluções de problemas ainda presentes na True Time.
27. Conclusão
Mesmo com a série de problemas ainda presentes na True Time API, ainda é
possível obter o melhor dela quando trabalhando junto a outros componentes de
linguagem de programação, componentes como o bloco try{}catch{}.
A simplicidade na requisição e retorno de um objeto Date válido faz com que não
tenhamos de depender de um algoritmo nosso ou até mesmo de um servidor
remoto, em nossa gerência, para o trabalho com uma data e horário NTP.
Necessitando da True Time API em algum de seus projetos, não deixe de
continuar acompanhando as issues e a evolução desta biblioteca. Os
mantenedores da True Time são bem ativos e vêm evoluindo ela.
28. Fontes
Conteúdo completo, em texto e em vídeo, no link a seguir:
- https://www.thiengo.com.br/true-time-api-para-data-e-horario-ntp-no-android
Fontes:
- https://tech.instacart.com/offline-first-introducing-truetime-for-swift-and-
android-15e5d968df96
- https://github.com/instacart/truetime-android
- https://ntp.br/ntp.php
29. Para estudo
- Treinamento oficial:
- Prototipagem Profissional de Aplicativos Android.
- Meus livros:
- Receitas Para Desenvolvedores Android;
- Refatorando Para Programas Limpos.
- Redes:
- Udemy;
- YouTube;
- Facebook;
- LinkedIn;
- GitHub;
- Twitter;
- Google Plus.
- Blog App.
30. True Time API Para Data e Horário
NTP no Android
thiengo.com.br
Vinícius Thiengo
thiengocalopsita@gmail.com