Por que eu devo ler este artigo:Neste artigo vamos conhecer algumas das APIs do HTML5 mais utilizadas para que a sua próxima aplicação web ofereça os mesmos recursos de uma solução nativa. Para isso, inicialmente vamos falar um pouco sobre a evolução da web e, em seguida, abordaremos a web atual, apresentando trechos de código e dicas para que você economize tempo ao trabalhar com o HTML5 em uma aplicação inteiramente web para dispositivos móveis.

Desde a sua origem, a web como atualmente conhecemos passou por diversas modificações, principalmente em suas linguagens, ferramentas e arquiteturas. Como consequência, ao longo do tempo as aplicações produzidas tornaram-se mais complexas, com novas funcionalidades, modelos de interação e elementos visuais. Para exemplificar, no início dos anos 2000 contávamos com websites com conteúdo estático e layouts simples. Caso recordemos, em geral as páginas eram constituídas de uma composição de gifs, links que eram simples apontadores para outras páginas e várias composições de elementos <tr> e <td>, utilizados primordialmente para posicionar elementos na tela. Além disso, algumas delas ainda continham um conteúdo em formato de áudio mid tocando em segundo plano, o que garantia um ar de modernidade na época.

As páginas que queriam inovar um pouco mais e fornecer uma experiência mais rica ao usuário recorriam à utilização de plug-ins, como os que permitiam animações Flash (Adobe) ou applets Java (Oracle). Entre os browsers mais populares, podíamos contar com o Internet Explorer, em sua versão 8, e o Netscape, que mais tarde daria origem ao Firefox. O Google Chrome, por sua vez, ainda não existia, muito menos o Safari.

Ademais, a comunicação do cliente com o servidor funcionava através de requisições que forçavam o recarregamento completo da página, não explorando nenhum tipo de cache de recursos estáticos. A solução para esse problema, embora já empregada em alguns sites, só viria a ser oficializada cinco anos mais tarde, em 2005, através de um artigo escrito por Jesse Garrett, introduzindo o termo AJAX. Nessa mesma época, a arquitetura RESTful estava sendo proposta por Roy Fielding, o que seria outro grande avanço a ser adotado e que prevalece até os dias atuais, impulsionando as requisições AJAX. Através dessa arquitetura, pode-se realizar cache de recursos através da manipulação de informações nos cabeçalhos do protocolo HTTP.

Em meio a essas mudanças, notamos também a evolução dos navegadores, dos protocolos (basta lembrar que o protocolo HTTP está em sua segunda versão) e até mesmo das arquiteturas. A própria arquitetura cliente-servidor agora está sendo trabalhada para utilizarmos o servidor somente para a validação e persistência dos dados processados no cliente. Algumas vezes, no entanto, nem para esse fim ele será necessário, podendo ser chamado apenas no momento de carga inicial de recursos da aplicação, que rodará de forma off-line no cliente. Tal abordagem permite ganhos bastante significativos, uma vez que desafoga os servidores ao processar informações diretamente no cliente, o qual está ficando cada vez mais poderoso permitindo que essa prática se torne mais comum.

Uma vez que o servidor diminui parte do seu papel, o foco da evolução passa a ser os elementos que compõem o cliente: o browser e as tecnologias envolvidas, como JavaScript, HTML e CSS. O JavaScript, por exemplo, está em sua versão 1.8.5 e as engines que o interpretam estão ficando cada vez mais poderosas. Uma prova disso é que atualmente são encontrados mais de 15 projetos ativos de engines e um trabalho constante para que a experiência com a linguagem seja a mais fluída possível.

Por sua vez, o CSS atualmente está na versão 3, na qual diversas melhorias foram realizadas para facilitar a estilização e dar maior autonomia ao usuário, permitindo explorar também as características do dispositivo, como através da utilização de media queries, que permitem a utilização condicional de código CSS dependendo do tamanho da tela onde ele está sendo executado.

E, por fim, a linguagem HTML. Ela se encontra na versão 5 e constantemente recebe atualizações para simplificar a sua sintaxe, permitindo um maior poder de expressão e novas ferramentas para abranger os mais variados tipos de cenários, como melhorar a acessibilidade das páginas através dos elementos <section>, <nav>, etc. ou melhorar a visualização de elementos em uma página através da diretiva viewport do elemento <meta>.

Como vimos, JavaScript, CSS e HTML estão todos em constante evolução, e muito do que se tem feito para essa tríade está relacionado ao universo mobile, que trouxe mais desafios, uma vez que as aplicações agora podem trabalhar com os diversos recursos, como câmera, acelerômetro, etc. Além disso, agora elas são acessadas através de vários dispositivos, de características e modelos de integração distintos do mundo desktop. De fato, quando o universo mobile entrou em cena, deixamos de construir páginas para construirmos aplicativos web.

A nova arquitetura das aplicações web

Com a chegada dos dispositivos mobile, a forma como construímos uma aplicação web teve que ser repensada. Uma vez que ela também precisa rodar nesses dispositivos, torna-se importante garantir a sua otimização e fluidez, assim como geralmente acontece em uma aplicação nativa. Para isso acontecer, é importante nos atentarmos a conceitos como rich client, single page e ao uso bem pensado de HTML5 e CSS3.

Uma aplicação rich client representa uma solução que possui grande parte do código executada no cliente. Tal abordagem permite alguns ganhos bastante significativos, pois desafoga os servidores através da redução do número de requisições, o que, por sua vez, permite que a aplicação se torne mais fluída, pois o tempo de resposta será encurtado.

Já a utilização de Single Page Application (ou aplicação de uma única página) sugere que a aplicação seja renderizada em apenas uma página web, que jamais é recarregada completamente durante as requisições. Para que a atualização do conteúdo ocorra, são utilizadas requisições AJAX que trazem informações para modificar apenas algumas porções da tela. No caso de uma aplicação onde apenas parte do conteúdo é atualizado, essa abordagem permite o reaproveitamento de várias estruturas, garantindo um cenário de otimização de recursos e, consequentemente, desempenho.

Unido ao conjunto de boas práticas para a construção de uma aplicação web inserida no contexto mobile, entra em cena o HTML5, que é a grande especificação que está tornando a web cada vez mais poderosa e funcional. Através dessa tecnologia temos acesso a APIs que fazem a ligação entre as funções do dispositivo nativo e a aplicação web, permitindo construir uma solução web que se comporte de forma semelhante a uma aplicação nativa.

O HTML5 para o desenvolvimento de aplicações Mobile

Ao desenvolver uma aplicação mobile, podemos optar por uma tecnologia nativa, uma tecnologia web ou a combinação das duas. Em se tratando de desenvolvimento nativo, geralmente realizado através de Objective-C para iOS, Java para Android e C# para Windows Phone, dificilmente uma empresa se dá ao luxo de produzir aplicações específicas para cada um dos dispositivos e plataformas alvo. Optando por minimizar o custo do projeto, normalmente são adotados frameworks que possibilitam escrever apenas um código e compilá-lo (ou interpretá-lo) para os demais ambientes. Esses frameworks adotam a prática “Write Once Run Anywhere” (WORA) e existem diversos produtos no mercado, como o Appcelerator, Adobe AIR, Sencha, Unity, Corona e Mono, que permitem fazer isso.

No entanto, mesmo com a ajuda dessas opções, a construção dessas aplicações WORA ainda é cara, visto que grande parte dessas tecnologias é proprietária e o mercado brasileiro ainda carece de mão de obra especializada em cada uma delas, principalmente quando nos esbarramos em bugs mais complexos durante o desenvolvimento.

Dito isso, vamos nos concentrar no desenvolvimento web (que visa simplesmente uma aplicação que roda em um browser, utilizando consequentemente JavaScript, HTML e CSS). Se pararmos para pensar, já possuímos tecnologias multidispositivo e multiplataforma (ver BOX 1) sendo desenvolvidas há mais de 20 anos e, portanto, com uma estabilidade e maturidade mais conhecida.

Uma aplicação web que executa sobre browsers, como o Chrome, Firefox, Internet Explorer e Opera, é, por definição, multidispositivo e multiplataforma. Note que os browsers funcionam nos mais variados dispositivos (incluindo TVs) e também nas mais variadas plataformas. Então, para que reinventar a roda se esforçando para construir ferramentas que permitem o desenvolvimento multidispositivo e multiplataforma se o próprio browser faz esse papel? De fato, ele age como um proxy, provendo uma camada de abstração entre o dispositivo e a aplicação, o que permite trabalharmos apenas nessa camada e abstrairmos grande parte dos detalhes relacionados à plataforma onde a solução irá executar.

Um dos problemas dessa abordagem é que constantemente precisamos ultrapassar essa camada e acessar alguma funcionalidade/recurso específico do dispositivo, como a câmera ou a agenda de contatos. Para isso, o browser deve permitir o acesso à funcionalidade através de uma API JavaScript.

A definição sobre o que o browser pode acessar no dispositivo é determinada por especificações regidas pela W3C, principal organização de padronização da Internet. A partir disso, as empresas mantenedoras dos browsers podem decidir se vão seguir a especificação e implementá-la (completa ou parcialmente), criar sua própria especificação (desvinculando-se da W3C) ou mesmo não implementar nenhum recurso previsto. Atualmente, no entanto, as funcionalidades que normalmente são utilizadas em aplicações corporativas já se encontram disponíveis na maioria dos browsers e em conformidade com as especificações da W3C. Desse modo, já conseguimos construir aplicações web que funcionam off-line, persistem dados no próprio browser, vibram quando o usuário tocar em algum botão, acessam a câmera do usuário e ainda detectam a sua localização.

BOX 1. Aplicações multidispositivo e multiplataforma

Aplicações multidispositivo são aquelas construídas para funcionar de forma similar em diversos dispositivos, como PCs, tablets, smartphones, TVs, etc. Já as aplicações multiplataforma são aquelas construídas para funcionar nas diversas plataformas existentes, como Windows, Linux, Android, etc. Quando dizemos que uma aplicação é multidispositivo e multiplataforma, ela executará em todos os dispositivos e plataformas previstas, normalmente produzindo as mesmas funcionalidades e alcançando o mesmo objetivo final.

Vantagens e desvantagens de optar pelo desenvolvimento web

Como apresentado anteriormente, podemos utilizar o HTML5 para o desenvolvimento de aplicações porque ele, entre todos seus benefícios, também oferece as ferramentas necessárias para acessar funcionalidades nativas do dispositivo. Um problema dessa abordagem é que grande parte do que precisamos do HTML5 só está disponível para os browsers mais atuais, o que não seria um problema caso o usuário atualizasse a versão do browser em seu dispositivo, correto? Errado. Caso desejemos disponibilizar nossa aplicação em uma loja virtual, como a Apple Store, Google Play ou Windows Store, devemos executá-la em um WebView, que é um browser pertencente ao código nativo, ou seja, aberto e controlado pela aplicação nativa. O problema disso é que até a versão 5.x do Android as engines dos browsers que estão rodando em um WebView somente são atualizadas quando o próprio sistema operacional é atualizado. Isso significa, por exemplo, que se quisermos rodar uma aplicação web encapsulada em um WebView e disponibilizá-la para dispositivos Android 2.3, executaremos nossa aplicação em um browser antigo, lançado a seis anos atrás. O mesmo acontece para a família de dispositivos iOS e Windows Phone.

Mas isso quer dizer que estamos limitados a desenvolver aplicações web somente para dispositivos novos? Não. A especificação do WebView para Android, iOS e Windows Phone oferece a possibilidade de comunicação do JavaScript com o código nativo do dispositivo através da passagem de objetos. Algumas empresas dedicaram grande esforço para utilizar essa especificação e deixar esse processo de comunicação com o ambiente nativo o mais natural possível. Um exemplo disso é o antigo PhoneGAP, comprado pela Apache e rebatizado como Cordova, que é um projeto open source e que permite invocar funções como vibração do dispositivo, acesso à agenda de contatos, geolocalização, etc. através de uma API JavaScript. Ao adotar essa opção podemos construir um proxy JavaScript que chaveará a nossa chamada de função para uma chamada JavaScript que pode, por sua vez, invocar uma API HTML5 ou então uma API do Cordova, caso a API HTML5 não seja suportada pelo browser, oferecendo assim uma maneira elegante de abstração de código.

Problema resolvido? Não. Embora atualmente isso não ocorra, era comum em versões antigas do Android e iOS ter o desempenho da aplicação que estava rodando em um WebView propositalmente diminuído, principalmente a velocidade de execução de animações CSS e processamentos pesados em JavaScript. Isso era feito exatamente por questões estratégicas, para evitar que o desenvolvimento web se alastrasse em detrimento do desenvolvimento nativo, onde as linguagens e bibliotecas de cada plataforma normalmente são utilizadas. Então, caso ainda desejemos construir aplicações para dispositivos antigos, devemos lembrar que os mesmos são limitados, o que nos força a construir a nossa aplicação da forma mais otimizada possível, ficando atentos às funções do CSS que podemos utilizar (em particular, o CSS3 é bastante limitado nesses dispositivos) e também à construção de códigos JavaScript otimizados, para que consigamos nos aproximar cada vez mais de uma experiência nativa.

Felizmente, por fim, existe uma solução que está recebendo bastante atenção ultimamente, permitindo subir junto com a aplicação uma instrumentação da API do WebView, ou seja, um preenchimento dos gaps da API para que o conjunto final seja completo, como se estivéssemos rodando a nossa aplicação em um browser atual. Pode-se dizer, inclusive, que essa solução permite disponibilizarmos junto com a aplicação o browser onde ela rodará. Esse é o projeto Crosswalk, cujo endereço pode ser encontrado na seção Links.

APIs do HTML5 para o desenvolvimento Mobile

A seguir, vamos explorar algumas das APIs do HTML5 comumente utilizadas ao se construir uma aplicação web mobile que deve oferecer recursos semelhantes às aplicações nativas. Veremos os pontos fortes e fracos de cada API, bem como exemplos de utilização e dicas para que o desenvolvedor não avance para caminhos sem saída ao utilizá-las. Não é intuito aqui apresentar todos os detalhes de cada API, analisando todos os seus métodos, contratos e formas de invocar funções. Isso está presente em suas documentações.

Ao se trabalhar com uma API do HTML5, primeiramente devemos verificar se ela é suportada pelo browser e se o usuário deu permissões suficientes para a execução da API. Em ambos os casos, caso a resposta seja negativa, a criação do objeto da API correspondente retornará nulo, sendo de extrema importância o desenvolvedor garantir que seu código tratará esses casos.

Além disso, muitas vezes uma API do HTML5 pode ser suportada por um browser e não ser suportada por outro. Para os casos onde não há suporte, algumas vezes ainda pode ser possível emular a funcionalidade através da composição de outras APIs. Assim, é uma boa prática a utilização de um conceito chamado Polyfill, que representa a criação de uma interface contendo os métodos da API e as consequentes implementações de cada método em cada browser, seja naqueles onde a API HTML5 oferece suporte total ou naqueles onde a funcionalidade é emulada. Dessa forma, podemos adotar uma API em um browser X e outra API em um browser Y, sendo que ambas compartilham a mesma interface, o que deixa o desenvolvedor em contato apenas com a interface previamente criada.

Ao escolhermos uma API HTML5 a ser utilizada em nossa aplicação, é sempre uma boa prática verificarmos quais browsers oferecem suporte para ela, quais onde ela pode ser emulada e também quais onde não há suporte. Uma boa dica para a execução dessa tarefa é navegarmos no site “Can I Use” (ver seção Links), que é um catálogo online de APIs que apresenta o suporte de cada uma delas nos browsers mais utilizados.

Persistência de dados

Através dessa funcionalidade podemos fechar a aplicação (ou mesmo desligar o dispositivo) e retornar com os dados persistidos quando a abrimos novamente. Até então, esse conceito é bastante simples, mas há uma grande diferença entre o universo das aplicações web tradicionais, onde geralmente armazenamos os dados em bancos de servidores remotos, e o universo das aplicações mobile, onde os dados estão armazenados no próprio dispositivo. Uma delas é que a latência de conexão e o volume de I/O é significativamente menor para os dispositivos mobile, o que nos permite trazer mais dados do banco sem que tenhamos perda de performance. Outra diferença é que o processamento da aplicação (regras de negócio, controle de telas, etc.) é executado no mesmo dispositivo onde ocorre o processamento das consultas de banco, por isso deve-se tomar cuidado com o número excessivo de operações para que a performance da aplicação não seja reduzida como um todo. Por fim, se no mundo web tradicional estamos acostumados a lidar com as mais variadas arquiteturas de banco (relacionais, orientadas a objetos, etc.) e as mais variadas tecnologias (MongoDB, Oracle, MySQL, PostgreSQL, etc.), no universo mobile estamos limitados a poucas opções: podemos utilizar para o iOS um banco relacional, como o SQLite, ou um não relacional, como o Core Data. Já no ambiente Android, uma das opções é o SQLite, e em um ambiente Windows Phone, o Local Folder.

Caso optemos por salvar os dados diretamente no browser, ao invés de recorrermos ao cenário nativo, temos algumas outras opções a considerar. Uma bastante comum é a utilização de cookies, que, embora não tenham sido criados para esse fim, permitem armazenar dados no browser do usuário e ainda trafegá-los na rede de forma simples. Da mesma forma, existe uma API chamada Web Storage para salvar dados com base em pares chave-valor. É importante observar que ambas as soluções não foram construídas para servir como um banco de dados, uma vez que elas não definem uma estrutura de dados bem definida que permita operações de pesquisa sobre os dados, indexação, etc. Para isso, podemos contar com duas alternativas: WebSQL e IndexedDB.

A API WebSQL

O WebSQL é uma API que permite a criação de bancos de dados relacionais no cliente e que é suportada pelos browsers listados na Figura 1.

A Figura 1 indica a compatibilidade das APIs HTML5 com base nas versões dos browsers representados nas figuras. Uma caixa vermelha indica que não há suporte para a API no browser, uma caixa amarela indica que há suporte parcial para a API e, por fim, uma caixa verde indica que o navegador implementa completamente a API, oferecendo suporte completo.

Suporte para a API WebSQL
Figura 1. Suporte para a API WebSQL – Fonte: can I use.

Inicialmente, devemos abrir a base de dados, conforme o código apresentado a seguir:

var db = openDatabase('todos1', '1', 'todo list example db', 2 * 1024 * 1024);

Esse código é executado de forma síncrona e retorna uma instância da base de dados. Os parâmetros necessários para a função serão apresentados a seguir:

  1. Parâmetro que informa o nome da base de dados;
  2. Parâmetro que informa a versão da base de dados, sinalizando ao usuário quando a estrutura da base foi alterada, caso isso aconteça;
  3. Parâmetro que descreve a base de dados;
  4. Parâmetro que especifica o tamanho estimado da base de dados.

Caso essa operação seja realizada com sucesso, podemos criar transações, que nos possibilitam o rollback das operações de inserção e remoção de registros. Veja um exemplo de execução de duas queries (a primeira para a criação de uma tabela e a segunda para a inserção de registros nessa tabela) através do trecho de código apresentado na Listagem 1.

Listagem 1. Executando códigos SQL em WebSQL.


 db.transaction(function (tx) {  
   tx.executeSql('CREATE TABLE IF NOT EXISTS foo (id unique, text)');  
   tx.executeSql('INSERT INTO foo (id, text) VALUES (1, "synergies")');  
 }); 

Embora apresentada aqui, a WebSQL está sendo descontinuada pela W3C, mas ainda é muito utilizada pois é a única que é suportada por alguns browsers mobile, principalmente o Safari. Essa API está cedendo espaço para a IndexedDB, que permite trabalhar com um banco não relacional e está sendo incorporada pelos browsers atuais. Como essa é uma API mais recente, não possui suporte para browsers antigos, conforme demonstra a Figura 2.

Suporte para a API IndexedDB
Figura 2. Suporte para a API IndexedDB – Fonte: can I use.

Para trabalharmos com a base de dados, inicialmente devemos abri-la, conforme o código apresentado a seguir:

var request = indexedDB.open("todos", "2.0 beta");

Esse código é executado de forma síncrona e retorna uma instância da base de dados. Os parâmetros necessários para a função são:

  1. Parâmetro que informa o nome da base de dados;
  2. Parâmetro que informa a versão da base de dados.

Da mesma forma que na WebSQL, na IndexedDB interagimos com a base de dados através de transações. Elas demarcam o momento de início e fim das operações de manipulação dos dados, que aqui não são realizadas através de queries, mas sim através de funções, como a inserção (put), remoção (delete) e recuperação (get) de registros. A Listagem 2 traz um exemplo que demonstra como inserir um item em uma tabela.

Listagem 2. Inserindo um registro na IndexedDB.

var db = todoDB.indexedDB.db;  
var trans = db.transaction('todo', 'readwrite');  
var store = trans.objectStore('todo');  
var data = {  
  "text": "sample text"
  "timeStamp": new Date().getTime()  
};  
var request = store.put(data);  
request.oncomplete = function(e) { .. }  
    request.onerror = function(e) { .. }  

Como pudemos perceber, existem duas principais APIs para trabalharmos com banco de dados no cliente. Uma boa prática nesse cenário é encapsularmos essas APIs em apenas uma, que automaticamente realiza as chamadas para a API correspondente dependendo do suporte do nosso browser. Existem diversas bibliotecas JavaScript que realizam esse trabalho, muitas vezes atuando como um Polyfill, onde programamos em uma das sintaxes (WebSQL ou IndexedDB) e o código pode ser executado em ambientes que suportam quaisquer uma das duas APIs.

Um outro problema comum ao trabalharmos com essas APIs é o limite do espaço necessário para salvarmos nossos dados. Como podemos perceber na Tabela 1, o espaço de armazenamento varia dependendo do browser e do dispositivo em execução. Em geral, o usuário recebe uma notificação perguntando se ele deseja alocar mais espaço para salvar os dados, entretanto, cada browser pode funcionar de uma forma diferente. Por exemplo, o Chrome trabalha com dois tipos de armazenamentos: o temporário e o persistente. No caso da IndexedDB ou WebSQL, ele somente possibilita trabalhar com o armazenamento temporário, o que permite até 10% do espaço livre onde o aplicativo está sendo executado, o que pode ser pouco, uma vez que os dispositivos mobile geralmente não possuem capacidade alta de armazenamento.

Chrome

Android Browser

Firefox

Safari

Safari

iOS WebView

iOS WebView

40

4.3

34

6, 7

8

6, 7

8

IndexedDB (desktop)

up to quota

50MB, Unlimited

up to quota?

10MB, 250MB(~999MB)

IndexedDB (mobile)

up to quota

5MB, Unlimited

up to quota?

up to quota?

WebSQL (desktop)

up to quota

5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB...

5MB, 10MB, 50MB, 100MB, 500MB, 600MB, 700MB...

WebSQL (mobile)

up to quota

200MB~

5MB, 10MB, 25MB, 50MB

5MB, 10MB, 25MB, 50MB

50MB

50MB

Tabela 1. Capacidade de armazenamento dos browsers.

Isso pode nos indicar que o armazenamento no cliente deve ser muito bem avaliado para não descobrirmos problemas críticos de forma tardia, onde geralmente grande parte do projeto já foi realizada. Por exemplo, não é uma boa prática guardarmos imagens nesses bancos de dados uma vez que o abuso dessa operação pode esgotar o espaço de armazenamento rapidamente. Além disso, deve ser criada uma estratégia para persistir os dados off-line em um banco em um servidor, uma vez que os dados no cliente são mais voláteis. Por exemplo, uma vez que o armazenamento temporário esteja esgotado, o Chrome automaticamente o limpará para alocar espaço para novos dados, o que pode trazer resultados catastróficos para a nossa base de dados.

As aplicações para a persistência dos dados no lado do cliente são muitas. Em particular, podemos pensar em robustez da aplicação, uma vez que podemos fazer um backup dos dados que serão enviados para o servidor. Por exemplo, suponha que estejamos completando um grande formulário de cadastro de dados e, no decorrer do processo, a nossa conexão cai ou a bateria do dispositivo acaba. Ainda assim podemos restaurar os campos preenchidos quando retornarmos à tela, bastando salvá-los em uma base local. Outro exemplo interessante consiste em realizar o cache de operações no lado do cliente. Podemos, por exemplo, armazenar dados lidos do servidor que são praticamente imutáveis, como a lista de estados de um país, as roles de um determinado usuário, etc. Na próxima consulta, podemos ler de nossa base local ao invés de consultarmos o servidor, mesmo que tenhamos um cache mais inteligente, a nível de protocolo.

Conectividade

A conectividade envolve a interação da aplicação com uma entidade que deseja trocar informações com ela. Um exemplo disso é quando requisitamos os dados estáticos (como arquivos HTML, CSS, JavaScript, imagens, etc.) do servidor para que nossa aplicação os processe. Nesse caso, estamos nos referindo à conexão geralmente mediada pelo protocolo HTTP, mas também podemos utilizar outros protocolos, como é o caso dos WebSockets, que utilizam diretamente o protocolo TCP. Além disso, também temos o cenário onde não há conectividade alguma, o qual também está se tornando comum uma vez que a web também nos possibilita a criação de aplicações standalone, que serão executadas unicamente no dispositivo do usuário sem a necessidade de interação com o servidor. De uma forma ou de outra, as seções seguintes abordam formas de se trabalhar com a presença ou ausência de conectividade e as APIs que podemos utilizar para isso.

Comunicando com o servidor

Como mencionado anteriormente, podemos nos comunicar com o servidor para recuperarmos os arquivos que devem ser processados no cliente, bem como processarmos as regras de negócio da nossa aplicação e eventualmente guardarmos as informações necessárias em um banco de dados. Podemos nos comunicar com o servidor através do JavaScript, utilizando um objeto denominado XMLHttpRequest, que nos permite realizar um request e tratar os dados do response. A evolução dessa API, trazida pelo HTML5, é a XMLHttpRequest 2.

O nome dessa API é um pouco confuso, uma vez que ela permite a conexão HTTP e também HTTPS. Além disso, ela não trafega somente XML, mas vários outros formados, incluindo JSON e textos puros. Apesar disso, com a nova API, temos os seguintes benefícios:

  1. Possibilidade de configurar timeout de requests;
  2. Melhorias para se trabalhar com objetos Form Data;
  3. Possibilidade de se transferir objetos binários;
  4. Possibilidade de monitorar o progresso da transferência de dados;
  5. Possibilidade de se realizar requests seguros entre browsers;
  6. Sobrescrever o media type e o encoding de responses.

Ela é suportada pelos browsers listados na Figura 3.

Suporte para a API XMLHTTPRequest2
Figura 3. Suporte para a API XMLHTTPRequest2 – Fonte: can I use.

Todas essas melhorias são importantes para o desenvolvimento, principalmente para evitarmos os diversos workarounds que costumávamos fazer para suportar as features que a API XMLHttpRequest antiga tinha. Entretanto, para o desenvolvimento mobile, as melhorias que mais impactam são a possibilidade de se transferir objetos binários e a possibilidade de se realizar requests seguros entre browsers, pois ambas impactam diretamente na construção de aplicações single page.

Anteriormente, caso desejássemos realizar o envio de objetos binários para o servidor sem o recarregamento total da página, poderíamos utilizar duas estratégias: a utilização de um iframe ou de um plugin flash. De uma forma ou de outra, não era a melhor estratégia, uma vez que o iframe não permitia o acompanhamento intuitivo de eventos de conclusão do download e a utilização de um plugin flash está sendo descontinuada.

WebSockets

Através da API de Web Sockets podemos estabelecer uma conexão entre o servidor e o cliente de forma full-duplex, ou seja, o cliente pode enviar dados para o servidor e vice-versa, ao contrário do HTTP, que permite uma conexão unidirecional, onde somente o servidor envia dados ao cliente. Através dessa conexão, podemos trafegar dados entre ambas as pontas através de um socket. Essa API é muito poderosa, uma vez que elimina a necessidade de realizarmos um polling do cliente para o servidor, como costumamos fazer através do HTTP, geralmente consultando o servidor regularmente para descobrirmos se uma determinada informação foi alterada. Através da API, o servidor agora pode notificar o cliente e enviar os dados desejados, bem como pode receber requisições do cliente, assim como era de costume no HTTP.

Uma vez que uma conexão for estabelecida com o servidor, podemos enviar dados para o cliente ou para o servidor chamando a função send() e escutar esses dados através de um evento onMessage(), que pode ser construído no cliente ou no servidor. Essa API se tornou tão importante que o JEE 7 decidiu mantê-la em sua especificação, permitindo chamar as funções send() e onMessage() no servidor através de sua própria API. A Figura 4 indica quais browsers são compatíveis com a API.

Suporte para a API WebSockets
Figura 4. Suporte para a API WebSockets – Fonte: can I use.

Trabalhando offline

Aplicações web consistem de recursos da web que precisam ser acessados a partir de uma rede. Para que isso aconteça, deve haver uma conexão. No entanto, existem muitos casos em que os usuários não podem se conectar a uma rede devido a circunstâncias além de seu controle. Felizmente, existem APIs que conseguem interceptar as requisições do usuário e realizar cache das mesmas. Essas APIs permitem uma navegação off-line da aplicação, uma vez que os recursos serão lidos do cache quando não houver conexão. Quando uma conexão for detectada, essas APIs podem automaticamente atualizar o cache dos recursos estáticos, garantindo uma navegação mais rápida para o usuário, uma vez que os recursos serão lidos localmente na maioria das vezes. Existem duas principais APIs que podem ser utilizadas para esse fim: a AppCache e os Service Workers.

A API AppCache permite, através de um arquivo manifesto (que pode ser atualizado regularmente pelo autor do aplicativo), controlar quais arquivos estáticos serão geridos, isto é, monitorados, para saber se eles devem ser recuperados do servidor ou do cache. O processo funciona da seguinte forma: ao se conectar a uma rede pela primeira vez, um browser web irá ler o arquivo de manifesto, baixar os arquivos referenciados nele e armazená-los localmente. Então, na ausência de uma conexão de rede, o browser utilizará as cópias locais, fazendo com que tenhamos uma aplicação off-line. Por fim, caso desejemos forçar a atualização dos arquivos do cache, podemos editar o arquivo de manifesto e alterar alguma linha em seu conteúdo, fazendo com que todo o cache seja revalidado. Essa API, embora largamente suportada pelos browsers, atualmente está sendo descontinuada e cedendo espaço para uma nova API, chamada Service Workers. A Figura 5 indica quais browsers são compatíveis com a API AppCache.

Suporte para a API AppCache
Figura 5. Suporte para a API AppCache – Fonte: can I use.

Embora JavaScript seja mono thread, os Service Workers permitem rodar um script em segundo plano no browser, possibilitando realizar operações em paralelo. Quando um worker é disparado, ele pode processar uma determinada função e se comunicar com a thread principal retornando o resultado do processamento. Existem muitas aplicações para a utilização de workers, como realizar em segundo plano notificações push, geolocalização dinâmica (geo-fencing), etc.. Entretanto, um dos exemplos mais utilizados é permitir uma experiência de aplicação off-line ao usuário, sendo essa funcionalidade o foco dessa seção.

Os Service Workers trabalham bem em qualquer contexto, principalmente em sites com várias páginas (multi page application), uma vez que um Service Worker pode ser cadastrado para um domínio e opera em todas as requisições realizadas debaixo daquele domínio. Por se tratar de uma nova especificação, essa API ainda não é suportada largamente pelos browsers, veja a Figura 6.

Suporte para a API Service Workers
Figura 6. Suporte para a API Service Workers – Fonte: can I use.

Com os Service Workers, podemos realizar:

  • Caching (substitui a API AppCache);
  • Notificações Push (fornece notificações nativas para Android/Chrome);
  • Fetch (substitui a API XMLHttpRequest);
  • Sync (fornece atualizações em background);
  • Geofencing (fornece informações sobre a geolocalização do usuário).

Para registrarmos um Service Worker em uma página HTML, devemos proceder conforme a Listagem 3.

Listagem 3. Registrando um Service Worker.

if ('serviceWorker' in navigator)   
{  
  navigator.serviceWorker.register('service-worker.js', {scope: "./"}).then(function(registration)   
  {  
    console.log('Service Worker registrado com sucesso: ', registration.scope);  
  }).catch(function(err)   
  {  
    console.log('Erro no registro do Service Worker: ', err);  
  });  
    }  

Note que na linha 3 temos a referência para o arquivo service-worker.js. O conteúdo dele está na Listagem 4.

Listagem 4. Registrando um Service Worker.


 this.onfetch = function(event)   
 {  
   responseText = 'URL interceptada!';  
   event.respondWith(new Response(responseText));  
 };  

Esse é um exemplo simples que cadastra um Service Worker responsável por interceptar todas as URLs que se originam do site e responder com a String “URL interceptada! ”. Note o evento onfetch, que é uma das interfaces da API, responsável por interceptar qualquer requisição originada pela aplicação.

No browser Chrome, podemos inspecionar os Service Workers registrados visitando o endereço chrome://inspect/#service-workers. Além disso, caso queiramos depurar um código que está em execução, podemos verificar as threads em execução e inspecionar o Service Worker desejado. Observe esse comportamento na Figura 7.

Inspecionando um Service Worker no Chrome
Figura 7. Inspecionando um Service Worker no browser Chrome.

Notificações

Enviar, receber e exibir notificações são tarefas comuns em um dispositivo. Entre essas tarefas, receber uma mensagem de forma automática, isto é, sem a criação explícita de um mecanismo para ouvir mensagens (como um pooling), faz parte das notificações push. Até o momento, realizar notificações push não era possível através da maioria dos browsers, sendo, portanto, delegada para a implementação nativa fazê-lo. Entretanto, agora podemos realizar essa tarefa de forma simples através da combinação das APIs Service Workers, Web Notifications e Push API.

Para que possamos receber uma notificação push, primeiramente devemos registrar um Service Worker, assim como é feito na Listagem 3. Em seguida, utilizaremos a Push API (suportada pelos browsers listados na Figura 8) para registrar nossa aplicação, permitindo que escutemos as notificações push que chegarem para o nosso dispositivo. O código está na Listagem 5.

Suporte para a API Push
Figura 8. Suporte para a API Push – Fonte: can I use.
Listagem 5. Registrando a Push API.


navigator.serviceWorker.register('serviceworker.js', {scope:"./"}).then(
function(registration)   
{  
  navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {  
    serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true })  
      .then(onPushSubscription)  
      .catch(function(e) {  
        ...  
      });  
        }); 
      }).catch(function(err)   
      {  
        ...  
    });  

Caso executemos o trecho de código, notaremos que o browser solicitará o GCM (Google Cloud Manager) sender id, que é o identificador do projeto que pode ser cadastrado ao visitarmos o console do GCM. Para informá-lo, devemos incluir uma entrada no arquivo manifest.json assim como descrito no trecho de código a seguir:


  {  
    "gcm_sender_id": "653317226796"  
  }  

Em seguida, devemos importar o arquivo de manifest em nossa página, assim como descrito na Listagem 6.

Listagem 6. Importando o arquivo manifest.json.

<!doctype html>  
  <html lang="">  
    <head>  
      <link rel="manifest" href="manifest.json">  
    </head>  
    ...  
</html>  

Após isso, podemos receber as notificações que forem enviadas para a GCM, mas ainda devemos cadastrar um evento para escutar as notificações. Isso é feito dentro do Service Worker cadastrado anteriormente, de acordo com a Listagem 7.

Listagem 7. Recebendo um evento de push através de um Service Worker.

self.addEventListener('push', function(event) {  
  var notificationOptions = {  
    body: event.data  
  };  
  new Notification("Atenção", notificationOptions);  
}); 

Ao receber a notificação, como mencionado na Listagem 7, podemos exibi-la na área de notificação do dispositivo que o usuário está utilizando. Por exemplo, caso o usuário esteja em um dispositivo mobile, essas mensagens serão exibidas em sua barra de notificação, funcionando como uma push notification. Porém, se o usuário estiver em um computador desktop, essas mensagens serão exibidas como um pop-up em sua área de trabalho. Essa API é a “Web Notifications”, que foi utilizada quando construímos o objeto Notification, e é suportada pelos browsers listados na Figura 9.

Suporte para a API Web Notifications
Figura 9. Suporte para a API Web Notifications – Fonte: can I use.

Entrada de Dados

Além de outros sistemas, uma aplicação web mobile também deve se comunicar com o usuário que a utiliza, recebendo informações e processando os dados recebidos. Os dispositivos mobile e as aplicações que são executadas neles possuem modelos de interação diferentes dos que podemos realizar através do teclado e mouse. Neles, temos a tela touchscreen, câmera, acelerômetro, microfone, etc. Felizmente, existem várias APIs do HTML5 que podem ser utilizadas para acessar o hardware do dispositivo e prover o acesso para que a aplicação possa trabalhar com eles.

Sem dúvidas, a utilização dessas APIs abriu um novo rumo para as aplicações web mobile. Em particular, entre as APIs mencionadas, a API de geolocalização e a API para o acesso à câmera e ao microfone ganharam bastante foco através do HTML5.

A API de geolocalização permite à aplicação web acessar dados relativos à localização do usuário. Essa API é implementada em praticamente todos os browsers, com a exceção do Internet Explorer 8 e o Opera Mini. Veja a Figura 10.

Suporte para a API Geolocalization
Figura 10. Suporte para a API Geolocalization – Fonte: can I use.

Como exemplo, observe o código da Listagem 8, onde acessamos a latitude e a longitude do usuário corrente.

Listagem 8. Acessando a latitude e a longitude do usuário corrente.

 if (navigator.geolocation)   
 {    
   navigator.geolocation.getCurrentPosition(function(position)  
   {  
     alert("Latitude: " + position.coords.latitude + "<br>Longitude: " + position.coords.longitude);  
   });    
 }

Como vimos, a utilização dessa API é bastante simples. Entretanto, o que a torna interessante é a sua combinação com outras APIs, nos oferecendo a possibilidade de explorar funcionalidades como o posicionamento do usuário em um mapa, a exibição de informações customizadas, o cálculo da velocidade média com que o usuário realiza um determinado percurso, etc.

Saindo do exemplo da geolocalização e explorando a combinação com outras APIs, vamos observar a combinação da API de Canvas, Service Workers e a API de getUserMedia. Através delas podemos realizar uma funcionalidade muito requisitada, que antes era delegada para a implementação nativa e agora possui suporte para ser executada dentro de um browser: a leitura de código de barras.

A API de Canvas permite desenhar gráficos, fazer composição de fotos, criar animações ou até mesmo fazer processamento de vídeo em tempo real. O suporte dela é mostrado na Figura 11.

Suporte para a API Canvas
Figura 11. Suporte para a API Canvas – Fonte: can I use.

A API de objeto de getUserMedia nos oferece acesso ao objeto de media stream, o que permite o acesso instantâneo aos dados, em segundo plano. Isso quer dizer que a câmera pode estar ligada e ao mesmo tempo podemos coletar os dados produzidos por ela e processá-los. O suporte dela é mostrado na Figura 12.

Suporte para a API GetUserMedia
Figura 12. Suporte para a API GetUserMedia – Fonte: can I use.

Combinando essas APIs, podemos realizar a leitura de um código de barras, uma vez que conseguimos ler os dados da câmera através da API de getUserMedia, plotá-los em um Canvas através da API de Canvas e realizarmos o processamento da imagem em segundo plano através da API de Service Workers. O leitor encontrará diversas bibliotecas que realizam esse trabalho, e, embora cada uma implemente esse comportamento de forma diferente, todas utilizam essas três APIs e seguem os mesmos princípios mencionados até aqui.

Vimos neste artigo um pouco da evolução do desenvolvimento web ao longo do tempo: novas especificações foram adicionadas, suportes antigos foram removidos e até a arquitetura foi remodelada. Entre todas essas mudanças, notamos que o desenvolvimento para web está cada vez mais próximo de ser universal e, portanto, de oferecer suporte total para a construção de aplicações que rodem em todas as plataformas e dispositivos. Nesse sentido, o HTML5 é a peça fundamental, servindo como uma grande especificação que está em constante evolução.

Vale lembrar que se o leitor desejar construir uma aplicação que deve rodar em browsers antigos, ele deve adotar como prática constante a consulta a sites como o “can I use” para não correr o risco de descobrir tardiamente que sua aplicação não conseguirá suportar todas as suas funcionalidades previstas. Isso se agrava ainda mais caso o leitor deseje publicar sua aplicação em uma loja como o Google Play, Apple Store ou Windows Store, onde ele provavelmente deverá encapsular sua aplicação para ser executada em um WebView, que é bastante precário em dispositivos antigos, abaixo do Android 5 e iPhone 6. Apesar disso, caso estejamos olhando para o futuro, podemos dizer que é uma aposta segura confiarmos no desenvolvimento Web como um caminho para sua aplicação ser multiplataforma e multidispositivo.