Crie aplicativos Ajax para a Web móvel

Construa aplicativos da Web para smartphone de navegador cruzado

Desenvolver para dispositivos móveis tem um custo alto, proposta de retorno baixo por muitos anos, apesar da novidade em torno disso. A mais nova geração de smartphones acionados pelo iPhone OS e Android do Google fornece uma solução mais simplificada: só construa aplicativos da Web. Isso oferece a você uma construção para abordagem de todos os dispositivos, o que pode diminuir o custo. E, ainda melhor, todos esses dispositivos de high-end oferecem navegadores ultramodernos que suportam HTML avançado, Javascript e CSS. Neste artigo, aprenda como construir aplicativos pesados de Asynchronous JavaScript and XML (Ajax) que aproveitam ao máximo as capacidades dos smartphones modernos. Você aprenderá não apenas como obter o máximo desses dispositivos, mas também como lidar com as sutis diferenças entre eles.

Michael Galpin, Software architect, eBay

Michael_GalpinMichael Galpin é uma arquiteto do eBay e contribui com frequência para o developerWorks. Ele foi o palestrante em várias conferências técnicas, incluindo JavaOne, EclipseCon e AjaxWorld. Para uma pré-visualização do seu trabalho atual, siga @michaelg no Twitter.



30/Mar/2010

Introdução

Este artigo explica como desenvolver um aplicativo móvel da Web destinado ao Apple iPhone, assim como a smartphones baseados em Android. Para desenvolver aplicativos móveis da Web diante de tais dispositivos, você não pode apenas usar seus navegadores normais de desktop, pelo menos não completamente. Você precisará de emuladores ou dispositivos reais. Para o iPhone, você precisa do emulador de iPhone. Ele é parte do iPhone SDK. Neste artigo, foi usado o iPhone SDK 3.1. Se modo semelhante, você precisará do Android SDK. Ele inclui o gerenciador Android Virtual Device, que pode ser usado para criar emuladores Android executando várias versões de Android. Neste artigo, foi usado o Android SDK 2.1. A maior parte do código neste artigo é Javascript, com um pouco de HTML e CSS acrescentados. Há um aspecto de lado do servidor desse aplicativo que foi escrito em código Java™. Isso é arbitrário, e como com qualquer outro aplicativo da Web, você pode usar qualquer tecnologia de lado do servidor que desejar. Para executar o aplicativo deste artigo, você precisará do Java 1.6. Você também precisará de Jersey, a implementação de referência de JAX-RS, e todos os arquivos Java Archive (JAR) associados (consulte Recursos para obter os links).


Navegadores móveis e WebKit

Dispositivos móveis têm navegadores da Web há anos. No entanto, desenvolver para eles sempre foi um problema porque os desenvolvedores da Web tiveram que lidar com questões de navegador cruzado. Muitos dias longos e noites em claro foram necessários para que o HTML, JavaScript e CSS trabalhassem de forma idêntica nas várias versões de Internet Explorer, Mozilla Firefox, Safari, e assim por diante. Bem, o mundo do navegador de desktop é muito comportado se comparado ao mundo do navegador móvel. O número de diferentes navegadores móveis tem sido, historicamente, impressionante. Cada produtor de dispositivo tinha seu próprio navegador e mesmo dispositivos do mesmo fabricante tinham grandes variações em sistemas operacionais, tamanhos de tela, e assim por diante. Alguns navegadores só suportavam WAP, outros suportavam subconjuntos de HTML, alguns continham HTML completo, mas não Javascript.

Felizmente, as coisas realmente mudaram. Desde janeiro de 2010, mais de 80% do tráfego de internet móvel nos Estados Unidos é feito em um iPhone ou dispositivo Android. Esses dois sistemas operacionais não apenas usam Webkit para renderização de HTML/CSS, mas também possuem mecanismos Javascript que adotaram os padrões HTML 5 de forma agressiva. Você leu certo. Os navegadores com posição dominante no espaço móvel também estão impulsionando padrões abertos. Você realmente não poderia pedir uma situação melhor do que essa como desenvolvedor da Web.

Ainda há variações entre navegadores, até mesmo entre diferentes versões do iPhone e navegadores Android. Isso é especialmente verdade para o Android. Em versões do Android anteriores à 2.0, o navegador Android usava a tecnologia proprietária do Google Gears. Ao Gears pode ser atribuída a impulsão de muitas inovações que agora se tornaram parte do padrão HTML 5. No entanto, isso significa que por um tempo, alguns padrões do HTML 5 não eram suportados no navegador Android, mas você podia usar o Gears para obter as mesmas funções. Todo o código neste artigo é baseado nos padrões HTML 5 e funcionará no Android 2.0+ ou no iPhone 3.0+. Agora que o palco está pronto para explorar esses modernos navegadores baseados no WebKit, dê uma olhada no Ajax nesses dispositivos.

Ajax em navegadores móveis

Exatamente como nos aplicativos da Web para desktop, a chave para criar uma experiência de usuário convincente em aplicativos móveis da Web normalmente envolve Ajax. É claro que a experiência do usuário não é o único motivo para usar Ajax; ela também pode ser mais rápida e eficiente. Esses são motivos ainda mais significativos para usar Ajax em um aplicativo móvel da Web, em que a latência de rede é muito maior, e o próprio navegador é muito mais limitado em termos de velocidade do processador, memória e tamanhos de cache. Felizmente, o Ajax se tornou uma das muitas coisas que foram facilitadas por ter que se preocupar somente com navegadores baseados em padrões. Antes de entrar nisso, vamos dar uma olhada rápida no servidor de back-end que o aplicativo que você irá criar neste artigo irá chamar.

Antes de começar, você precisará fazer o download dos arquivos JAR necessários para Jersey, Xerces, Rome, e do Google App Engine SDK (consulte Recursos para obter os links) e instalá-los na seguinte pasta: WebKitBlog>war>WEB-INF>lib. Você pode fazer o download do resto do código de origem do aplicativo.

O blog do WebKit

O aplicativo móvel da Web neste artigo é um aplicativo simples para ler notícias sobre desenvolvimento da Web móvel. Por ora, ele simplesmente manterá um feed RSS do blog oficial do WebKit, mas poderia ser facilmente modificado para agregar múltiplos feeds RSS. O aplicativo é um aplicativo Java da Web comum que você pode implementar em qualquer servidor de aplicativo Java. O código todo é mostrado na Listagem 1.

Listagem 1. A classe Feed
@Path("/feed")
public class Feed {
    String surfinSafari = "http://webkit.org/blog/feed/";
    
    @GET @Produces("application/json")
    public News getNews(@DefaultValue("0") @QueryParam("after") long after) 
    throws Exception{
      URL feedUrl = new URL(surfinSafari);
      SyndFeedInput input = new SyndFeedInput();
        SyndFeed feed = input.build(new XmlReader(feedUrl));
        List<Item> entries = new ArrayList<Item>(feed.getEntries().size());
        for (Object obj : feed.getEntries()){
            SyndEntry entry = (SyndEntry) obj;
            if (entry.getPublishedDate().getTime() > after){
                Item item = new Item(entry.getTitle(), entry.getLink(), 
                            entry.getDescription().getValue(), 
                            entry.getPublishedDate().getTime());
                entries.add(item);
            }
        }
        return new News(feed.getTitle(), entries);
    }
}

Esse código usa JAX-RS do Java para criar um serviço RESTful. A anotação @Path designa o terminal do serviço, ou seja, o URL relativo a esse serviço será /feed. @GET indica que o serviço suporta HTTP GET. A anotação @Produces declara que esse serviço produzirá dados em um formato JSON. Essa é uma maneira fácil de ficar com seus dados serializados em JSON. O método getNews pega um único parâmetro chamado after, ou seja, pega as entradas depois de uma certa data. Ele também usa as anotações JAX-RS para ligar o parâmetro after ao parâmetro de cadeia de caractere de consulta chamado after. Se não é dado nenhum valor, ele pega um valor padrão 0.

Até o momento, falei na Listagem 1 que são as anotações JAX-RS que configuram o roteamento e a serialização de dados para o serviço. O verdadeiro corpo do método depende em grande medida do pacote Rome para processar RSS. Ele simplesmente faz o download do feed RSS mais recente e então o transforma para apenas os dados de que você precisa para o aplicativo, modelados pelas classes Item e News. A única parte complicada é que os dados publicados do artigos são transformados em long e usados como um ID. Isso torna o ID especialmente útil porque você pode usá-lo para classificar, como verá mais adiante. A classe News é mostrada na Listagem 2.

Listagem 2. A classe News
@XmlRootElement
public class News {
    private String title;
    private List<Item> entries;
    // constructors, getters/setters omitted for brevity
}

Observe que a classe News usa a anotação JAXB @XmlRootElement. Não há XML nesse aplicativo, mas o JAX-RS impulsiona o JAXB para fazer a serialização/desserialização automática. Toda essa classe possui uma propriedade de título e uma lista de itens. A classe Item é mostrada na Listagem 3.

Listagem 3. Classe Item
@XmlType
public class Item {
    private String title;
    private String link;
    private String description;
    private Long id;
    // constructors, getters/setters omitted for brevity
}

Essa classe mostra a você exatamente o que você vai mostrar no aplicativo da Web. Como a classe News class, ela usa uma anotação JAXB para que JAX-RS possa serializá-la em JSON para você. Há uma última parte para o código de lado do servidor que é configurar seu aplicativo da Web para que as solicitações sejam roteadas para JAX-RS. Para isso você precisa editar o arquivo web.xml file do aplicativo, como mostrado na Listagem 4.

Listagem 4. O arquivo de configuração web.xml
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">
    <servlet>
        <servlet-name>WebKit Blog Servlet</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.
ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>org.developerworks.mobile</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>WebKit Blog Servlet</servlet-name>
        <url-pattern>/resources/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    <!-- Default page to serve -->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    <mime-mapping>
        <extension>mf</extension>
        <mime-type>text/cache-manifest</mime-type>
    </mime-mapping>
</web-app>

Ele é, na maioria, código web.xml clichê. Como é possível ver na declaração de servlet, o aplicativo está usando Jersey, a implementação de referência de JAX-RS. O parâmetro de inicialização do servlet diz ao Jersey para varrer o pacote org.developerworks.mobile em busca de classes que foram anotadas para manipular solicitações de serviço. Alem disso, qualquer solicitação que mapeie para /resources/ será mapeada para o servlet Jersey. A última coisa a se observar aqui é a seção de mapeamento mime no final do arquivo. Ele é o tipo MIME para evidenciar arquivos, uma chave para aplicativos da Web off-line que discutirei mais adiante. Agora que você viu o serviço de back-end que o aplicativo da Web irá usar, é hora de olhar o front-end que o usa.


Interface do usuário acionada por Ajax

Você deve ter notado na Listagem 4 que o aplicativo possui um arquivo index.html padrão. Ele é o ponto de entrada para o aplicativo, e é mostrado na Listagem 5.

Listagem 5. O arquivo index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html manifest="application.mf">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <title>WebKit News</title>
    <meta name = "viewport" content = "width = device-width"/>
    <script type="text/javascript" src="index.js"></script>
  </head>
  <body onload="loadEntries()">
    <h1 id="title">Recent News About WebKit</h1><img 
id="loader" src="loading.gif"/>
  </body>
</html>

Esta é uma página bem simples da Web, mas há algumas coisas bastante notáveis. Primeiro, observe que no cabeçalho do documento eu defini a porta de visualização Ela instrui o browser para ampliar o conteúdo para que apareça bem em um dispositivo. Em termos de código UI, há apenas um título e uma imagem de carregamento, só isso. Todo o resto é feito pelo Javascript. A função loadEntries no arquivo index.js faz uma solicitação Ajax para carregar os dados. Essa função é mostrada na Listagem 6.

Listagem 6. A função loadEntries
function loadEntries(){
    if (!window.JSON){
        var head = document.getElementsByTagName("head")[0];
        var jsScript = document.createElement("script");
        jsScript.setAttribute("src", "json2.js");
        jsScript.setAttribute("type","text/javascript");
        head.appendChild(jsScript);
    }    
      var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (this.readyState == 4 &&this.status == 200){
                  var theFeed = JSON.parse(this.responseText);
                  var i = 0;
                  if (theFeed.entries){
                         var len = theFeed.entries.length;
                         for (i=0;i<len;i++){
                                addEntry(theFeed.entries[len - 1 -i], true);
                         }
                  }
                  var body = document.getElementsByTagName("body")[0];
                  body.removeChild($("loader"));
              }
    };
    var urlStr = "/resources/feed";
    xhr.open("GET", urlStr);
    xhr.send();        
}

No começo dessa função, faço um pouco de detecção de recurso. A maioria dos navegadores suporta uma função incorporada para análise e serialização JSON. Ela é a mesma biblioteca JSON disponível em json.org. No entanto, se ela não estiver disponível, tudo o que você precisa fazer é incluir o arquivo na sua página, e você terá as mesmas capacidades.

De qualquer forma, ela é código Ajax bastante simples. Você não tem que se preocupar com o Internet Explorer, então você pode apenas criar um XMLHttpRequest diretamente. Ajuste sua propriedade onreadystatechange para uma função, criando um fechamento, neste caso. Quando a resposta retornar do servidor (readyState = 4) e se não houver problemas com o processamento (status = 200 OK), você então analisa a resposta JSON usando a função JSON.parse. Para cada entrada do feed, chame a função addEntry. Ela é uma função para criar os elementos UI para cada entrada. Ela é mostrada na Listagem 7.

Listagem7. A função addEntry
function addEntry(e, prepend){
    var eDiv = document.createElement("div");
    eDiv.setAttribute("class", "item");
    var link = document.createElement("a");
    link.setAttribute("href", e["link"]);
    var title = document.createTextNode(e["title"]);
    link.appendChild(title);
    eDiv.appendChild(link);
    var button = document.createElement("input");
    button.setAttribute("type", "button");
    button.setAttribute("value", "Show");
    button.setAttribute("id", "button" + e["id"]);
    button.setAttribute("onclick", "showDescription('" + e["id"] + "')");
    eDiv.appendChild(button);
    var dDiv = document.createElement("div");
    dDiv.setAttribute("id", e["id"]);
    dDiv.setAttribute("class", "description");
    dDiv.setAttribute("style", "display:none");
    dDiv.innerHTML = e["description"];
    eDiv.appendChild(dDiv);
    var body = document.getElementsByTagName("body")[0];
    if (!prepend && body.childNodes.length > 1){
            body.appendChild(eDiv);
    } else {
            body.insertBefore(eDiv, body.childNodes.item(2));
    }
}

Essa função é pura manipulação padrão do DOM. O único aspecto complicado é que ela pode anexar ou pré-anexar a entrada, ou adicionar a entrada na parte superior ou inferior. Voltando à Listagem 6, a última coisa que a função faz é remover o gráfico de carregamento. Isso conclui as funções Ajax básicas, e, dependendo do seu caso de uso, é o suficiente. No entanto, o aplicativo requer que seja feito o download de muitos dados do servidor e que sejam analisados. Você pode fazer melhor, e, em um aplicativo móvel da Web, você realmente precisa usar armazenamento em cache local.


Armazenamento em cache com armazenamento local

O armazenamento local é parte da especificação HTML 5 e é amplamente suportado. Ele fornece um nome simples - mecanismo de armazenamento de par de valor, em que o nome e os valores são cadeias de caractere. Assim, salvar as entradas em armazenamento local é simples e é mostrado na Listagem 8.

Listagem 8. Salvando em armazenamento local
function saveLocal(entry){
    if (!window.localStorage){
        return;
    }
    localStorage.setItem(entry["id"], JSON.stringify(entry));
}

Mais uma vez, faça um pouco de detecção da capacidade do navegador, verificando primeiro se o objeto de janela possui a propriedade localStorage, de acordo com a especificação HTML 5. Se possuir, então use apenas o id da entrada como chave para armazenar. Para o valor, você precisar usar uma cadeia de caractere, então use a função JSON.stringify para serializar o objeto como uma cadeia de caractere. Agora você só precisa de uma função para ler todos esses dados do armazenamento local. Ela é mostrada na Listagem 9.

Listagem 9. Carregando do armazenamento local
function loadLocal(){
    if (!window.localStorage){
        return [];
    }
    var i = 0;
    var e = {};
    var id = 0;
    var entries = [];
    for (i=0; i< localStorage.length; i++){
        id = localStorage.key(i);
        e = JSON.parse(localStorage.getItem(id));
        entries[entries.length] = e;
    }
    entries.sort(function(a,b) { 
        return b.id - a.id; 
    });
    return entries;
}

Novamente, essa função começa verificando se o armazenamento local está disponível ou não. Em seguida, ela itera todos os dados em armazenamento local. Para cada valor em armazenamento, use novamente a função JSON.parse para analisar a cadeia de caractere de volta a um objeto. Depois classifique as entradas, pois a ordem em que elas vêm do armazenamento local não é garantida. Ela faz uma classificação descendente, colocando as entradas mais novas primeiro. Finalmente, agora que você possui funções para armazenar e carregar do armazenamento local, você só precisa integrá-lo de volta à função loadEntries, como mostrado na Listagem 10.

Listagem 10. Adicione armazenamento em cache a loadEntries
function loadEntries(){
    // check for JSON object
    var localEntries = loadLocal();
    var newest = 0;
    if (localEntries.length > 0){
        newest = localEntries[0].id;
    }
    var i = 0;
    for (i=0;i<localEntries.length;i++){
        addEntry(localEntries[i]);
    }
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200){
                  var theFeed = JSON.parse(this.responseText);
                  var i = 0;
                  if (theFeed.entries){
                         var len = theFeed.entries.length;
                         for (i=0;i<len;i++){
                                addEntry(theFeed.entries[len - 1 -i], true);
                                saveLocal(theFeed.entries[len - 1 -i]);
                         }
                  }
                  var body = document.getElementsByTagName("body")[0];
                  body.removeChild($("loader"));
              }
    };
    var urlStr = "/resources/feed?after=" + newest;
    xhr.open("GET", urlStr);
    xhr.send();    
}

A maior diferença aqui é que primeiro você carrega os dados locais. Determine a entrada mais recente que você tem, então, quando chamar o servidor, use esse parâmetro de modo que o servidor envie somente entradas que ainda não estão disponíveis do armazenamento local. Finalmente, no retorno de chamada, quando pega as entradas, você precisa chamar saveLocal para salvá-las em cache local. Com novas entradas armazenadas localmente, você agora está em uma boa posição para ativar acesso off-line completo ao seu aplicativo da Web.


Trabalhando off-line

Ativar um aplicativo para trabalhar off-line frequentemente é considerado o Santo Graal do desenvolvimento de aplicativo móvel da Web. Embora possa ser complicado, muitos dos novos recursos na HTML 5 removeram muitos obstáculos. A primeira coisa que você precisa fazer é identificar quais ativos são necessários off-line e listá-los no manifesto do aplicativo, como mostrado na Listagem 11.

Listagem 11. Manifesto do aplicativo
CACHE MANIFEST
index.html
index.js
json2.js
loading.gif

Para um aplicativo simples como este, o manifesto também é bastante simples. Ele contém todos os arquivos estáticos: HTML, Javascript e imagens (CSS também, caso fosse preciso). É importante que esse arquivo seja servido com um tipo MIME de manifesto em texto/cache—exatamente como você configurou no arquivo web.xml na Listagem 4. Finalmente, se você olhar novamente a Listagem 5, verá que o elemento html raiz possui um atributo manifest que aponta para o arquivo de manifesto na Listagem 11. A última coisa que você precisa fazer é uma última modificação na função loadEntries, como mostrado na Listagem 12.

Listagem 12. Verificar off-line
function loadEntries(){
    // load local
    // ...
    // check if online
    if (navigator.onLine){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            if (this.readyState == 4 && this.status == 200){
                      var theFeed = JSON.parse(this.responseText);
                      var i = 0;
                      if (theFeed.entries){
                             var len = theFeed.entries.length;
                             for (i=0;i<len;i++){
                                    addEntry(theFeed.entries[len - 1 -i], true);
                                    saveLocal(theFeed.entries[len - 1 -i]);
                             }
                      }
                      var body = document.getElementsByTagName("body")[0];
                      body.removeChild($("loader"));
                  }
        };
        var urlStr = "/resources/feed?after=" + newest;
        xhr.open("GET", urlStr);
        xhr.send();    
    }
}

Para ver se você está on-line ou off-line, verifique a propriedade onLine do objeto do navegador (window.navigator). Se você não estiver on-line, então não há sentido em chamar o servidor para pedir novas entradas, apenas exiba todas as locais. Agora você possui um aplicativo móvel da Web completo que é capaz de operar off-line.


Resumo

Os aplicativos móveis da Web são um tópico recente atual, por um bom motivo. Eles fornecem uma forma de atingir usuários em muitos tipos diferentes de dispositivos. Eles aproveitam o nível mais alto de padronização entre navegadores que os desenvolvedores da Web jamais aproveitaram. Neste artigo, você viu todas as técnicas essenciais necessárias para criar um aplicativo móvel da Web que depende de Ajax. E, dependendo de Ajax, você é capaz de aproveitar os recursos de ponta nos navegadores móveis. Apenas olhe o código de exemplo neste artigo. Você poderia ter gerado facilmente todo o HTML no servidor quando a página foi solicitada. No entanto, colocando toda a UI no cliente e puxando dados do servidor usando Ajax, você pode armazenar dados em cache facilmente no cliente e até ativar o aplicativo para trabalhar off-line.


Download

DescriçãoNomeTamanho
Article source codeWebKitBlog.zip32KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=478564
ArticleTitle=Crie aplicativos Ajax para a Web móvel
publish-date=03302010