Guerra dos navegadores do Android e iPhone, Parte 2: Construa um aplicativo baseado em navegador para iPhone e Android

Usando HTML 5, CSS, JavaScript, Ajax e jQuery

Este artigo é o segundo da série de duas partes chamada "Guerra dos navegadores do Android e iPhone" sobre o desenvolvimento de aplicativos baseados em navegador para iPhone e Android. Na Parte 1, apresentamos o WebKit, o mecanismo de navegação que está na essência no iPhone e Android. Neste artigo, vamos um pouco mais a fundo construindo um aplicativo de gerenciamento de rede que é executado tanto nos navegadores do iPhone como do Android. O aplicativo demonstra o armazenamento SQL local no navegador e o Ajax, tecnologias-chave que possibilitam uma experiência de aplicativo rico no navegador móvel. Além disso, o aplicativo usa a conhecida biblioteca jQuery JavaScript.

Mr. Frank Ableson, Software designer, IBM

Frank Ableson é empreendedor de desenvolvedor de software ao norte de Nova Jersey, sendo especializado em softwares remotos e softwares de aplicativos integrados. Ele é autor de um livro sobre o desenvolvimento de aplicativos para Android. Seus interesses profissionais são sistemas integrados, comunicação sem fio e eletrônicos automotivos. Seus maiores fãs são sua esposa, Nikki, e seus filhos. Entre em contato com ele pelo endereço fableson@msiservices.com.



05/Jan/2010

Apresentação

Historicamente, são encontrados diversos obstáculos para o desenvolvimento de aplicativos remotos da Web repletos de recursos, mas esse panorama está mudando rapidamente. O desafio da velocidade de rede insuficiente ainda não foi eliminado, mas tem sido bastante minimizado uma vez que a tecnologia 3G está se tornando amplamente disponível para telefones celulares. Da mesma forma, a interface do usuário tem se desenvolvido muito, com o iPhone liderando o mercado e o Android ganhando impulso com designs inovadores e um poderoso mecanismo de WebKit que fornece renderização HTML e CSS de primeira classe. Oferecer ao usuário uma interface ruim não mais significa que o trabalho foi concluído.

Assim como a experiência de navegação em desktop, os aplicativos remotos da Web vêm passando por uma profusão de novos tipos, uma vez que o armazenamento em banco de dados local tornou-se disponível para o navegador. Uma outra tecnologia que capacita aplicativos remotos da Web de nível avançado é o Ajax. O Ajax descreve a prática de usar JavaScript para chamar uma página do lado do servidor para recuperar elementos de dados específicos via HTTP sem a necessidade de recuperar e renderizar novamente toda a página da Web. Uma vez que o Ajax funciona de maneira assíncrona, aplicativos remotos da Web ganharam um tremendo impulso para a capacidade dos recursos.

Neste artigo, nos concentramos no iPhone e no Android. Para auxiliar no desenvolvimento, também testaremos no aplicativo no Safari, no desktop. Devido ao fato de o Safari também ser baseado em WebKit, ele é uma plataforma de desenvolvimento ideal, acelerando o desenvolvimento e fornecendo um excelente auxílio para depuração, graças ao útil aplicativo WebKit Inspector.


O aplicativo

Antes de passarmos às especificidades do código, vamos dar uma olhada nas finalidades do aplicativo. O que desejamos obter com nosso aplicativo de monitoramento/gerenciamento de rede?

Se você já gerenciou um Web site para clientes — seja interno ou externo —, provavelmente já recebeu uma notificação de que o site estava inativo. Essa notificação pode ter vindo de uma proativa ferramenta de monitoramento automatizada ou, às vezes, de uma variação menos desejável: um e-mail ou telefonema do seu cliente avisando que "O site está inativo. Verifique isso e entre em contato comigo o mais rápido possível".

Geralmente, qual a primeira coisa a fazer quando você descobre que o site está inativo? Você abre o navegador e tenta carregar a página inicial. Talvez a indisponibilidade esteja relacionada à conectividade de apenas um local. Isso ocorre às vezes, e vale a pena verificar antes de se aprofundar mais para encontrar um problema que, talvez, não esteja realmente no site em si.

Se excluirmos a possibilidade de um problema básico de conectividade do cliente, a próxima etapa na ordem de prioridades de análise do site é obter alguns detalhes-chave, como recursos do sistema de arquivos, memória disponível, variação de conectividade, última mensagem de erro etc. Isso geralmente requer acesso ao próprio servidor pelo Desktop Remoto ou sessão SSH. Mas e se você não estiver em sua mesa?

Nunca é divertido receber a notificação "o site está inativo". Invariavelmente isso acontece no momento mais inoportuno, quando você está longe do escritório e sem conectividade normal com a Internet. Isso pode ser muito desolador. Com o tempo, você aprende que um determinado site tende a falhar devido a uma de várias causas suspeitas usuais. O problema pode ser que um recurso externo, como um banco de dados ou processador de pagamento de terceiros está indisponível; ou talvez tenha havido falha em uma transferência de arquivo, exibindo uma parte do Web site com informações inadequadas ou desatualizadas. Seja qual for o desafio, existem algumas estatísticas fundamentais que são úteis na determinação inicial de um problema. Essas estatísticas ou indicadores de desempenho variam de site para site. Construir uma ferramenta para ajudar a abordar esse problema é o objetivo do aplicativo que estudaremos neste artigo.

O aplicativo discutido aqui é projetado para ajudar na determinação de problemas do Web site quando você estiver longe do escritório. Se você tem um aparelho iPhone ou Android, acaba de obter um bom poder de fogo. Podemos aproveitar os recursos dessas poderosas plataformas para ajudar a gerenciar nossos Web sites.


Considerações sobre design

Uma das motivações do design deste aplicativo é ser o mais independente possível do banco de dados de um servidor — em outras palavras, queremos que este aplicativo seja executado sem a necessidade de criar uma conta em um serviço de terceiros. Iremos construir este aplicativo de forma que o usuário precise transferir a página da Web por download apenas uma vez e possa executá-la localmente em seu navegador. É claro que o usuário pode adicionar a página aos favoritos e atualizá-la conforme desejado para obter quaisquer novos recursos possivelmente adicionados ao aplicativo com o passar do tempo. No entanto, todos os dados são armazenados localmente no dispositivo remoto em um banco de dados SQL.

Embora certamente haja um mérito em ter esse tipo de dado armazenado em um servidor em outro lugar, a funcionalidade necessária para gerenciar e armazenar dados para diversos usuários está fora do escopo deste artigo. Nossa primeira preocupação é usar armazenamento local e Ajax para construir um aplicativo prático e útil. Iremos concluir com algumas etapas lógicas para expandir o aplicativo.

O código para este aplicativo pode ser quebrado em dois grupos distintos. Temos um código que é executado no dispositivo remoto e que inclui:

  • Um arquivo index.html que é o shell do nosso aplicativo.
  • Um arquivo JavaScript chamado netmon.js que contém a maior parte da funcionalidade do aplicativo.
  • Outro arquivo JavaScript chamado json2.js que contém rotinas relacionadas ao JSON.
  • Vários arquivos CSS contêm as informações de estilo. Lembre-se do que foi dito na Parte 1: grande parte das informações de estilo estão contidas no arquivo CSS principal. No entanto, temos arquivos CSS específicos do dispositivo para auxiliar no refinamento da aparência e dos recursos do aplicativo em uma determinada plataforma.
  • Incluímos o arquivo da biblioteca jquery.js para ajudar na manipulação do DOM e nas consultas Ajax.

Em seguida, temos o código que é executado no servidor e é exclusivo para cada site que desejamos gerenciar. As especificidades deste código irão variar em sua implementação, mas o conteúdo resultante é sempre o mesmo: um objeto JavaScript Object Notation (JSON) que contém algumas propriedades específicas, incluindo itens com o nome da propriedade, que é um array de pares nome-valor. Os dados do objeto JSON indicam como ele será renderizado no navegador de seu aplicativo. Além disso, os pares nome-valor fornecem dados operacionais fundamentais que são específicos para cada site, fornecendo (assim se espera) as informações necessárias para definir rapidamente a situação e ajudar a equipe de suporte a localizar e solucionar imediatamente o problema.


Construindo o aplicativo

Começamos observando o modelo de dados deste aplicativo. Assim que temos uma noção do modelo de dados, examinaremos o código que interage com os dados, dando vida ao aplicativo.

O modelo de dados

Quando os dados são armazenados no dispositivo remoto pelo navegador, eles são armazenados em um banco de dados HTML 5, ou em um armazenamento do banco de dados SQL acessível ao navegador. A especificação dos dados SQL baseados no navegador ainda estão em processo de mudança e suas características ainda estão sendo alinhadas. No entanto, em termos práticos, atualmente a especificação está disponível para uso no iPhone, Android e em outros ambientes habilitados para WebKit. Essa funcionalidade do banco de dados é, essencialmente, uma interface JavaScript para implementação SQLite subjacente. Nosso banco de dados contém apenas uma única tabela: tbl_resources.

A tabela imita os dados que nosso aplicativo usa no tempo de execução — essencialmente um array de objetos JSON. Cada objeto contém:

  • O nome de um site.
  • O URL da página inicial do site.
  • As estatísticas-chave do URL do site; chamamos isso de URL de ping.
  • O status atual do site: OK ou BAD.
  • O resumo atual do site, que é um pequeno trecho de texto descrevendo a condição atual do site. Isso pode dar a impressão de que o banco de dados está inativo, por exemplo.
  • Uma matriz de pares nome-valor que contém detalhes específicos do site para auxiliar na descrição das condições operacionais atuais do site. Observe que esses valores podem ser válidos mesmo quando o site não está inativo. Um determinado elemento pode conter dados que fornecem informações sobre um problema iminente em um futuro próximo.

A Listagem 1 é um exemplo de objeto JSON que representa um site.

Listagem 1. Objeto JSON representando um site
    [
      {
         name : 'msi-wireless.com',
         homeurl : 'http://msiservices.com',
         pingurl : 'http://ibm.msi-wireless.com/mobile2/netmon.php',
         status : 'OK',
         summary : 'Everything is fine...',
         items :
         [
          {name : 'DiskSpace', value : '22.13 GB'},
          {name : 'Database Up?', value : 'Yes'}
         ]
      }
   ]

A função do banco de dados é persistir os dados com o passar do tempo, embora a manipulação seja feita por uma matriz que contém esses objetos e não pela referenciação contínua do banco de dados. Esta estratégia foi implementada para simplificar um pouco as coisas no JavaScript e para minimizar a quantidade de vezes que os dados entram e saem do banco de dados. No entanto, para um aplicativo com grandes quantidades de elementos de dados, pode ser vantajoso trabalhar diretamente com o banco de dados ou, mais provavelmente, em algum tipo de forma "paginada", em que uma certa quantidade de elementos é retirada do banco de dados por vez e uma matriz JavaScript contém uma "janela" de elementos, representando um subconjunto do número total de itens.

A Figura 1 contém uma captura instantânea da estrutura do banco de dados e de alguns registros. É possível visualizar o banco de dados com o Web Inspector, que é parte da plataforma do navegador Safari/WebKit. Este é um dos motivos pelos quais o desenvolvimento com o WebKit tem grande eficácia. Aqui estamos observando o Web Inspector no desktop. Todo o código também funciona bem no iPhone e Android.

Observação: Este código foi criado para o Android V2.0. O recurso WebKit definido no Android foi desenvolvido para cada release.

Figura 1. Captura instantânea da estrutura do banco de dados e alguns registros
Screenshot shows a snapshot of the database structure and a few records

Agora que conhecemos a aparência dos elementos de dados, como iremos gerenciá-los com nosso aplicativo?


Lado do cliente — A visualização padrão

A interface do usuário padrão do aplicativo é uma lista de sites sob gerenciamento, classificados pelo valor de seu status. Os sites que não têm status OK são mostrados primeiro, como se vê na Figura 2.

Figura 2. Sites que não estão OK
Image shows sites that are not OK

Vemos que há três sites sob gerenciamento. No momento, temos dois sites indicando um problema. Se um site estiver em boas condições, significando que a propriedade do seu status equivale a OK, não exibimos o campo de resumo, e ele é exibido em texto na cor preta. Se o status for BAD, exibimos o resumo ao lado do nome do site e um estilo chamado BAD no arquivo CSS indica os atributos de renderização — neste exemplo, o texto em vermelho. Para obter mais detalhes, consulte o arquivo netmon.css; o código de origem completo deste aplicativo está disponível na seção Downloads.

Quando uma entrada é clicada, os detalhes dessa entrada alternam-se entre visíveis e ocultos. Conforme visto na Figura 2, cada entrada possui três links disponíveis, seguidos pela localização inicial e do URL de ping. Em seguida, há uma seção que lista os detalhes do site, que é a lista de pares nome-valor que representam as condições daquele site.

Neste exemplo, vemos que o site chamado ibm demo 2 tem o resumo "No more coffee?" É claro que não se trata necessariamente de uma emergência técnica, mas temos um divertido exemplo a ser considerado. Passando à seção Details desta entrada, vemos as principais estatísticas por trás da condição deste servidor: A cafeteira está vazia.

Vemos que é possível clicar no link da página inicial, que abrirá uma nova janela do navegador. Em segundo lugar, atualizamos os dados clicando no link Refresh. Examinaremos a etapa Refresh em breve.

Por fim, podemos remover esta entrada selecionando o link Remove. Uma simples consulta window.confirm() solicita a confirmação de que desejamos realizar esta tarefa irreversível.


Lado do cliente -- O HTML

Para criar esta lista, precisamos examinar detalhadamente dois arquivos. O primeiro é o index.html, mostrado na Listagem 2, e o segundo é netmon.js, mostrado na Listagem 3. Ao longo deste artigo, analisaremos alguns fragmentos, embora seja possível consultar a seção Downloads para obter o código de origem completo.

Ainda estamos especificando o meta da porta de visualização remota do WebKit a fim de ajudar a orientar o navegador para a forma de renderização preferida para a página. Também usamos um pouco de JavaScript seletivo para especificar um arquivo CSS em particular visando um dispositivo específico.

A novidade é a inclusão de alguns arquivos JS junto com alguns arquivos JavaScript "locais". O arquivo é refinado com um novo div HTML com o ID entryform, que contém os elementos necessários para que uma nova entrada seja adicionada diretamente de dentro do aplicativo. Não há a necessidade de carregar uma página HTML diferente, que é a abordagem tradicional para a realização dessa tarefa. Lembre-se de que um dos nossos objetivos de design é que nosso aplicativo não conte com ferramentas adicionais baseadas no servidor para a manipulação de dados ou armazenamento externo aos sites que estamos monitorando. A Listagem 2 contém as instruções de inclusão do arquivo index.html.

Listagem 2. index.html
<link rel="stylesheet" href="netmon.css" type="text/css" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="netmon.js"></script>
<script type="text/javascript" src="json2.js"></script>

<script type="text/javascript">
   if (navigator.userAgent.indexOf('iPhone') != -1) {
      document.write('<link rel="stylesheet" href="iphone.css" type="text/css" />');
   } else if (navigator.userAgent.indexOf('Android') != -1) {
      document.write('<link rel="stylesheet" href="android.css" type="text/css" />');
   } else {
      document.write('<link rel="stylesheet" href="desktop.css" type="text/css" />');
   }

Lado do cliente — Preenchendo a lista

Os dados são extraídos do nosso banco de dados local e exibidos em uma lista de entradas do servidor. Para obter a lista, abrimos o banco de dados e emitimos uma consulta SQL para buscar todos os itens da tabela do nosso banco de dados.

Um técnica comum em aplicativos deste tipo é criar tabelas durante a execução. Não se esqueça de que temos o DBA para nos ajudar. Nosso aplicativo deve fazer todo o trabalho sozinho. A Listagem 3 contém a origem para o netmon.js: o arquivo que implementa diversos itens, incluindo:

  • Definição de um construtor de tipo de dados para o objeto netmonResource.
  • Um grupo de funções necessárias para auxiliar o aplicativo na interação com o banco de dados e com os Web sites a serem monitorados. Isso é implementado no espaço de nomes netmon e contém:
    • dbHandle— Uma função usada para gerenciar nossa conexão ao banco de dados.
    • initialize— Uma função usada para criar/abrir o banco de dados e recuperar a lista de servidores que estamos gerenciando. A Listagem 3 contém esta função, que tenta abrir o banco de dados (caso ainda não esteja aberto) e consultá-lo. Se a tabela de banco de dados não estiver presente, esta função inicia o processo de criá-la.
    • createResourcesTable— Função que cria a tabela de banco de dados necessária e cria também uma única entrada padrão para fins de demonstração.
    • addEntry— Função que insere uma nova entrada na tabela de banco de dados.
    • deleteEntry— Função que remove uma entrada da tabela de banco de dados.
    • refreshEntry— Função que atualiza a entrada recuperando o URL de ping da entrada por meio de uma chamada Ajax.
    • Resources— Um array que contém um "cache" das entradas do nosso banco de dados. Parte das outras funções atuam neste array e não diretamente no banco de dados.
    • Render— Função que retorna uma cadeia de caractere HTML, formatada de acordo com algumas regras que planejamos sobre como preferimos que cada entrada do servidor seja exibida. Se tivermos novas ideias de formatação, elas serão implementadas nesta função, junto com quaisquer estilos CSS necessários.
Listagem 3. netmon.js

Clique aqui para ver lista de códigos

Listagem 3. netmon.js

  initialize : function () {
		try {
         if (netmon.dbHandle == null) {
		      netmon.dbHandle = openDatabase("netmondb","1.0","netmon",1000);
		   }
		   
		   $("#mainContent").html("");
	      netmon.dbHandle.transaction(function(tx) {
			   tx.executeSql("SELECT * FROM tbl_resources order by status,nme",
			     // parameters
				 [],
				 // good result
				 function(tx,result){
				 	try {
				 	   var items = new Array();
						for (var i = 0; i < result.rows.length; i++) {
							var row = result.rows.item(i);
							items[i] = new netmonResource();
							items[i].name = row['nme'];
							items[i].homeurl = row['homeurl'];
							items[i].pingurl = row['pingurl'];
							items[i].status = row['status'];
							items[i].summary = row['summary'];
							items[i].items = eval(row['items']);
							if (items[i].items == undefined) {
							   items[i].items = [];
							}
						}
						netmon.resources = items.slice(0);
		   // setup gui with our data				
                  if (netmon.resources.length > 0) {
                     jQuery.each(netmon.resources,function (index, value) {
                        $("#mainContent").append(netmon.render(index,value));
                     });
                     $(".serverentry").click (function() {$(this).find(".serveritems").toggle();});
                     $(".serveritems").hide();
                  }
					} catch (e) {
					  alert("Error fetching rows from database..." + e);
					}
				 },
				 function(tx,error){
				 	//alert("bad result on query: " + error.message + " 
                               let's try to create the db table");
					netmon.createResourcesTable();
				 }
				 ); 
   		});
		}catch (e) {
		   alert("error opening database.  " + e);
		}
      
   },
...
   render : function(index,itm) {
      try {
         var ret = "";
         ret += "<div class='serverentry " + itm.status + " " + (index % 2 == 0 ? 'even' : 'odd') + "'>";
         //ret += "<span class='name'>" + itm.name +
         "</span>&nbsp;&nbsp;<a target='_blank' href='" +
         itm.homeurl + "'>Show</a><br /><a target='_blank'
         href='javascript:netmon.deleteEntry(\"" + itm.name + "\");'>Remove</a><br />";
         ret += "<span class='name'>" + itm.name + "</span>";
         if (itm.status != "OK") {
            ret += "<span class='summary'>-" + itm.summary + "</span><br />";
         }
         
         ret += "<div class='serveritems'>"; 
         ret += "<a target='_blank' href='" + itm.homeurl + "'>Home</a>&nbsp;&nbsp;
         <a target='_blank' href='javascript:netmon.refreshEntry(" + index + ",false); '>Refresh</a>&nbsp;&nbsp;
         <a target='_blank' href='javascript:netmon.deleteEntry(\"" + itm.name + "\");'>Remove</a><br />";
         ret += "Home URL:&nbsp;" + itm.homeurl + "<br />";
         ret += "PING URL:&nbsp;" + itm.pingurl + "<br />";
	 ret += "<hr />Details<br />";
         jQuery.each(itm.items,function (j,itemdetail) {
            ret += ">>" + itemdetail.name + "=" + itemdetail.value + "<br />";
         });
         ret += "</div>";      
         ret += "</div>";
         return ret;
      } catch (e) {
            return "<div class='error'>Error rendering item [" + itm.name + "] " + e + "</div>";
      }
   }
};

O espaço de nomes netmon contém grande parte da lógico necessária para implementar o site, além de algumas funções auxiliares no arquivo index.html. Vamos dar uma olhada na tarefa de adicionar uma nova entrada ao banco de dados.


Lado do cliente — Adicionando um site

O aplicativo gerencia uma lista de um ou mais sites de interesse do usuário. Para cada site, inicialmente coletamos e armazenamos:

  • O nome do site
  • O URL inicial
  • O URL de ping

Para isso, criamos um formulário simples contendo três campos, seus rótulos e alguns botões, como mostra a Figura 3.

Figura 3. Formulário simples
Screen shows a simple form

O conteúdo deste formulário está, na verdade, contido na mesma página index.html, como mostra a Listagem 2. É possível alternar entre a visualização em lista padrão e este formulário para adição de novo servidor com um pouco de mágica do jQuery. A lista de entradas do site está contida em um div HTML com o ID mainContent. O formulário está localizado no div HTML com o ID entryform, que inicialmente está oculto. A qualquer momento, a visibilidade desses dois elementos div é mutuamente exclusiva. Para trocar sua visibilidade, chamamos o método jQuery chamado toggle em cada um desses elementos, como mostrado na função addNewEntry, exibida na Listagem 4.

Listagem 4. A função addNewEntry
function addNewEntry() {
   $("#entry_name").val("");
   $("#entry_homeurl").val("");
   $("#entry_pingurl").val("");

   $("#mainContent").toggle();
   $("#entryform").toggle();
}

Quando a entrada de um novo servidor é criada, é necessário salvá-la no banco de dados e atualizar nossa lista de servidores para refletir a inclusão da nova entrada na lista. O usuário começa selecionando o botão Save, que chama a função JavaScript saveEntry implementada no index.html. Esta função cria um novo objeto JavaScript chamado netmonResource. Confirmamos que todos os campos foram preenchidos corretamente e, em seguida, iniciamos a gravação do banco de dados. Salvamos a nova entrada chamando netmon.addEntry. Posteriormente, voltamos à lista de entradas chamando toggle novamente nos dois elementos div principais. Observação: Não implementamos a expressão regular completa para validar os URLs, o que seria uma boa ideia.

Para a discussão a seguir, consulte a Listagem 3: netmon.js.

O uso da interface do banco de dados SQL em JavaScript exige duas etapas básicas. Primeiro, é necessário abrir o banco de dados, caso ainda não esteja aberto. Neste aplicativo, abrimos o banco de dados na função initialize. Para criar um novo registro no banco de dados, vamos examinar o código na função addEntry, mostrada na Listagem 5. Uma transação SQL é iniciada chamando <databasehandle>.transaction(function (tx) {tx.executeSql()});. Em nosso caso, o identificador do banco de dados é netmon.dbHandle.

A função executeSql usa quatro argumentos:

  1. Uma instrução SQL na qual quaisquer parâmetros, como valores de campo, são representados com um marcador ?. Cada ? é substituído por um valor, com base na posição ordinal contida no segundo argumento da função.
  2. Um array de valores usados como parâmetros, substituindo os marcadores ? conforme indicado na instrução SQL no primeiro argumento.
  3. Uma função de retorno de chamada que é invocada caso a instrução seja executada com êxito. Os argumentos desta função de retorno de chamada incluem um indicador de transação e um conjunto de resultados.
  4. Uma função de retorno de chamada que é invocada caso a instrução encontre um erro. Os argumentos desta função de retorno de chamada incluem um indicador de transação e um objeto de erro.

A Listagem 5 mostra a função netmon.addentry.

Listagem 5. A função netmon.addentry

Clique aqui para ver lista de códigos

Listagem 5. A função netmon.addentry

   addEntry : function (entry) {
      try {
         netmon.dbHandle.transaction(function(tx) {
            tx.executeSql("insert into tbl_resources (nme,homeurl,pingurl,status,summary,items) values (?,?,?,?,?,?)",
               [
                  entry.name,
                  entry.homeurl,
                  entry.pingurl,
                  entry.status,
                  entry.summary,
                  JSON.stringify(entry.items)
               ],
               function (tx,results) {
                //  alert(entry.name + " added.");
                netmon.initialize();
               },
               function (tx,error) {
                  alert("Error in addEntry [" + error.message + "]");
               });
            });
      }catch (e) {
         alert("Error in netmon.addEntry " + e);
      }
   }

Agora, podemos adicionar entradas. A exclusão de entradas da lista é muito semelhante. Consulte a função netmon.deleteEntry para ver como uma entrada é excluída do banco de dados.

É hora de ver como atualizamos entradas com o Ajax.


Lado do cliente — Atualização com o Ajax

Conforme descrito anteriormente, o Ajax é uma técnica para recuperar conteúdo do lado do servidor sem que toda a página da Web seja atualizada. Isso é implementado com diferentes tecnologias subjacentes, dependendo do navegador. No entanto, fazemos com que a abordagem conte com a biblioteca jQuery do JavaScript. Sim, este é um artigo sobre desenvolvimento de navegadores para iPhone e Android mas, atualmente, é muito comum e aceito — e até mesmo incentivado — o uso de uma biblioteca JavaScript. Quando você descobrir como isso é simples no jQuery, provavelmente irá concordar.

A função netmon.refreshEntry contém nosso código Ajax, mostrado na Listagem 6, bem como um pouco de código para atualizar o banco de dados com os dados resultantes.

Listagem 6. A função netmon.refreshEntry

Clique aqui para ver lista de códigos

Listagem 6. A função netmon.refreshEntry

   refreshEntry : function (entryidx,bfollow) {
      try {
         //alert("refresEntry [" + netmon.resources[entryidx].name + "][" + netmon.resources[entryidx].pingurl + "]");
         $.get(netmon.resources[entryidx].pingurl,
            function (data,textstatus) {
              // alert("response is here : [" + data + "]");
               try {
                  var response = eval(data);

                  netmon.dbHandle.transaction(function(tx) {
                     tx.executeSql("update tbl_resources set status = ?,summary = ?,items = ? where nme = ?",
               [
                  response[0].status,
                  response[0].summary,
                  JSON.stringify(response[0].items),
                  netmon.resources[entryidx].name
               ],
               function (tx,results) {
                  netmon.initialize();
                  if (bfollow) {
                     if (entryidx + 1 < netmon.resources.length) {
                        netmon.refreshEntry(entryidx + 1,true);
                     }
                  }
               },
               function (tx,error) {
                  //alert("Error in refreshEntry [" + error.message + "]");
                  if (bfollow) {
                     if (entryidx + 1 < netmon.resources.length) {
                        netmon.refreshEntry(entryidx + 1,true);
                     }
                  }
                  
               });
            });

               } catch (e) {
                  alert("error handling response [" + e + "]");
               }
            }
            );
         
      } catch (e) {
         alert("Error refreshEntry " + e);
      }
      
   }

Para obter dados do servidor, basta chamar $.get(). O primeiro argumento é o URL, e o segundo é uma função a ser invocada quando a chamada for concluída. Em nosso exemplo, atualizamos o banco de dados com os dados recentemente recuperados (consulte a Listagem 3: netmon.refreshEntry) usando alguns dos recursos do banco de dados previamente discutidos.

O jQuery também fornece a capacidade de fornecer "dados codificados por formulário" para a transmissão de parâmetros para uma página da Web, bem como para a sugestão de que tipo de dado é esperado. Consulte os Recursos para obter uma referência sobre as funções Ajax do jQuery. Dependendo das necessidades do seu aplicativo, haverá uma granularidade adicional a ser obtida.

Observe que o jQuery também oferece um método para recuperar um objeto JSON diretamente. No entanto, escolhemos usar a função Ajax "genérica", que pode ser aplicada de forma mais geral a outros aplicativos que você construir.

Quando recebemos os dados do servidor, é necessário convertê-los em um objeto JavaScript para acessar suas propriedades. Isso pode ser feito de forma simples com a função JavaScript chamada eval: var object = eval(<json text>);.

Neste ponto, já concluímos quase toda a funcionalidade do lado do cliente. Resta um item para ser tratado: as páginas do lado do servidor, que são responsáveis pela geração do JSON que nosso aplicativo espera que seja processado.


Lado do servidor — Construa sua própria página de monitoramento

Descrevemos a estrutura JSON que o nosso aplicativo espera receber, mas como isso é gerado? A breve resposta é que depende de você. Se você preferir trabalhar com tecnologias .NET, é possível gerar uma página .aspx para gerar os dados dinamicamente. Como alternativa, é possível que um shell script planejado periodicamente gere um arquivo de texto que você simplesmente recupera para descobrir o status atualizado mais recentemente. O resultado é que o aplicativo precisa ser capaz de buscar um objeto JSON com base em um único URL, mas a realização da tarefa de gerar o JSON depende de você.

O arquivo PHP de exemplo mostrado na Listagem 7 gera uma lista de arquivos e procura seletivamente o arquivo chamado error.txt. Se este arquivo não estiver vazio, consideramos que há um problema de algum tipo, construímos nosso objeto JSON para indicar um valor de status BAD e usamos o conteúdo do arquivo error.txt para atualizar a propriedade do resumo.

Listagem 7. Arquivo PHP de exemplo
<?
$filecount = 0;
$statusValue = "OK";
$summaryValue = "No Error";
?>
[
{
items : [
{ "name" : "Date", "value" : "<? echo date("m/d/y H:m:s"); ?>"  },
<?
foreach (new DirectoryIterator("./data") as $fileInfo) {
   if ($fileInfo->isDot() || $fileInfo->isDir()) continue;
   $filecount++;
   echo "{\"name\" : \"".$fileInfo->getFilename()."\",\"value\" : 
\"".$fileInfo->getSize()."\"},";
   if ($fileInfo->getFilename() == "error.txt") {
     if ($fileInfo->getSize() > 0) {
        $statusValue = "BAD";
        $fsize = $fileInfo->getSize();
        $fh = fopen("./data/".$fileInfo->getFilename(),"r");
        $summaryValue = fread($fh,$fsize);
        fclose($fh);
     }
   }
}
?>
 {"name" : "FileCount","value" : "<? echo $filecount ?>"}
],
"status" : "<? echo $statusValue ?>",
"summary" : "<? print str_replace("\n","",$summaryValue) ?>"
}
]

Este código gera os seguintes dados JSON mostrados na Listagem 8, considerando que uma condição de erro esteja presente.

Listagem 8. Dados JSON gerados pela Listagem 4
[
    {
        "items" : [
            {
                "name" : "Date",
                "value" : "11/22/09 15:11:51" 
            },
            {
                "name" : "error.txt",
                "value" : "20" 
            },
            {
                "name" : "bad.html",
                "value" : "91" 
            },
            {
                "name" : "testfile.txt",
                "value" : "44" 
            },
            {
                "name" : "good.html",
                "value" : "87" 
            },
            {
                "name" : "FileCount",
                "value" : "4" 
            } 
        ],
        "status" : "BAD",
        "summary" : "the sky is falling." 
    } 
]

Neste exemplo, fornecemos a data na qual os dados foram gerados, uma contagem atual dos arquivos, bem como uma lista dos arquivos e seus tamanhos. Não se esqueça de que o fuso horário do seu servidor pode ser diferente daquele do seu local.

Uma vez que o arquivo error.txt tem tamanho diferente de 0 byte, lemos o conteúdo desse arquivo e o atribuímos à propriedade de resumo. É isso — um script de monitoramento extremamente simples.

Conforme você trabalha na geração do seu JSON, pode descobrir que há um problema na análise do código de dentro do seu aplicativo baseado em navegador. Se for esse o caso, use uma ferramenta de validação JSON (consulte Recursos). A Figura 4 mostra esta entrada renderizada no navegador do desktop.

Figura 4. Ferramenta de validação JSON
Figure 4 shows the JSON validation tool

A Figura 5 mostra o aplicativo sendo executado no iPhone.

Figura 5. Aplicativo sendo executado no iPhone
Image shows application running in iPhone

A Figura 6 mostra o aplicativo sendo executado no Android com listas de servidores ligeiramente diferentes.

Figura 6. Aplicativo sendo executado no Android
Figure 6 shows the application running in Android

O aplicativo agora está completo — bem, mais ou menos. Sempre há novas coisas que podem ser adicionadas, por isso, na próxima seção, que também é a última, vamos dar uma olhada em um lista de itens deixada como exercício para você.


Próximas etapas

Não faltam novas ideias que podem surgir para aprimorar este aplicativo. Veja aqui alguns itens que seria divertido adicionar:

  • Editar suas entradas — No momento, só há as opções Add New ou Delete.
  • Implementar algumas expressões regulares para validar corretamente entradas do campo do formulário.
  • Enviar informações a um colega por e-mail diretamente do aplicativo.
  • Armazenar perfis em um servidor para que seja possível mover-se de um dispositivo para outro.
  • Incorporar este aplicativo em um aplicativo nativo usando uma ferramenta como PhoneGap ou Appcelerator. Isso permitiria que você vendesse o aplicativo na AppStore.

Resumo

Este artigo foi criado a partir do conhecimento básico da Parte 1 com o intuito de criar um aplicativo remoto da Web altamente funcional usando armazenamento local SQL e consultas Ajax. O aplicativo em si pode ser usado como uma ferramenta para auxiliar no monitoramento das suas redes atuais com apenas um pouco trabalho no lado do servidor.

Espero que você tenha se inspirado para construir seus próprios aplicativos remotos para iPhone e Android.


Download

DescriçãoNomeTamanho
Part 2 source codeos-androidiphone2-browserwars2.zip31KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

  • O newsgroup Eclipse Platform deve ser sua primeira parada para tratar de perguntas sobre o Eclipse. (Se você selecioná-lo, será aberto seu aplicativo padrão de leitura de notícias Usenet bem como eclipse.platform.)
  • Os newsgroups Eclipse têm muitos recursos para pessoas interessadas em usar e estender o Eclipse.
  • Participe dos blogs developerWorks e envolva-se na comunidade do developerWorks.

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=469825
ArticleTitle=Guerra dos navegadores do Android e iPhone, Parte 2: Construa um aplicativo baseado em navegador para iPhone e Android
publish-date=01052010