Android и iPhone - войны браузеров: Часть 2. Разработка приложения для браузера на платформе iPhone или Android

Использование HTML 5, CSS, JavaScript, Ajax и jQuery

Эта статья - вторая в серии Android и iPhone: войны браузеров (EN), состоящей из двух частей и посвященной разработке приложений для браузеров iPhone и Android. В Части 1 мы познакомились с движком WebKit, обеспечивающим работу браузеров iPhone и Android. В этой статье мы продолжим наше знакомство с мобильными Web-технологиями и закончим разработку Web-приложения для мониторинга сети, которое будет выполняться на браузерах iPhone и Android. Наше приложение будет использовать локальное SQL-хранилище и технологию Ajax, обеспечивающую широкую функциональность приложений для мобильных браузеров. Кроме того, приложение будет использовать популярную JavaScript-библиотеку jQuery.

Фрэнк Эйблсон, проектировщик ПО, Независимый разработчик

Когда Фрэнк Эйблсон (Frank Ableson) закончил карьеру баскетболиста в команде своего колледжа, не заключив многолетнего контракта с «Лос-Анджедес Лейкерс», он занялся разработкой компьютерных программ. Он любит решать сложные задачи, особенно из области связи и интерфейсов с аппаратурой. Свободное время Фрэнк проводит со своей женой Никки и детьми. С ним можно связаться по адресу: frank@cfgsolutions.com.



20.10.2010

Введение

Еще недавно разработка многофункциональных мобильных Web-приложений сталкивалась с множеством препятствий, однако необходимо признать, что ситуация на рынке мобильных Web-технологий меняется очень быстро. Проблема низкоскоростных сетевых соединений не исчезла, однако использование технологий 3G в мобильных устройствах позволило значительно улучшить качество и скорость соединения. Использование инновационных дизайнерских находок iPhone и развитие систем Android позволили значительно улучшить функциональность и внешнее оформление элементов пользовательского интерфейса, а широкие возможности движка WebKit обеспечивают высокую эффективность использования HTML и CSS. Теперь уже вряд ли удастся заинтересовать пользователя мобильным приложением с минимальным набором функций и незатейливым дизайном пользовательского интерфейса.

Возможность использовать локальное хранилище данных на стороне браузера открыла новый этап в развитии приложений для настольных и мобильных браузеров. Еще одна технология, способствовавшая появлению нового поколения Web-приложений – это Ajax. Технология Ajax позволяет использовать JavaScript для обращения к странице на стороне сервера и получения определенных данных через HTTP без перезагрузки всей Web-страницы. Поскольку Ajax работает в асинхронном режиме, его использование позволяет значительно расширить возможности мобильных Web-приложений.

Приложение, рассматриваемое в данной статье, рассчитано на работу на платформах iPhone и Android. Для упрощения процесса разработки мы проверим работу приложения в браузере Safari на настольном компьютере. Поскольку Safari также основан на использовании WebKit, этот браузер является идеальной платформой для разработки мобильных приложений. Использование Safari позволяет значительно сократить и упростить процесс разработки приложений, а также воспользоваться средствами отладки, предоставляемыми программой WebKit Inspector.


Приложение

Прежде чем перейти к рассмотрению конкретных примеров кода, давайте обсудим основные задачи нашего сетевого приложения. Какие функции оно должно выполнять, и зачем вообще нужно подобное приложение для мониторинга сети?

Если вы когда-нибудь занимались поддержкой Web-сайта – внутреннего или внешнего, то вам, скорее всего, приходилось получать уведомления о сбоях в его работе. В лучшем случае подобные сообщения отправляются какими-либо автоматизированными средствами мониторинга сети еще до того, как проблема обнаружена пользователями. Возможны и другие, весьма нежелательные, сценарии развития событий, когда жалобы о сбое в работе сайта поступают непосредственно от пользователей по электронной почте или по телефону. Подобные жалобы, как правило, уведомляют о том, что поддерживаемый вами сайт недоступен, и требуют, чтобы проблема была решена в кратчайшие сроки.

Каковы будут ваши действия при получении уведомления о сбое в работе сайта? Прежде всего, вы запустите ваш браузер и попробуете зайти на главную страницу вашего сайта. Вполне возможно, что сайт функционирует в нормальном режиме, а проблема вызвана сбоем в сетевом соединении конкретного пользователя, т.е. сайт недоступен для какого-либо конкретного участка сети. Такое иногда случается, так что всегда имеет смысл проверить работу сайта, прежде чем углубиться в поиски несуществующей проблемы.

Если вы убедились в том, что проблема доступа к сайту действительно существует, в качестве следующего шага вы постараетесь собрать как можно больше данных о текущем статусе сайта, например, узнать состояние ресурсов файловой системы, объем свободной памяти, доступность различных соединений, последние сообщения об ошибках, и т.д. Сбор подобных данных, как правило, требует доступа к серверу через Remote Desktop (службу удаленного рабочего стола) или SSH-сессию. Но что, если в момент получения уведомления о проблемах в работе сайта вы находитесь не на своем рабочем месте?

Безусловно, никому не нравится получать жалобы или уведомления о сбое в работе сайта. Прежде всего, подобные сообщения приходят в абсолютно неподходящее время, когда вы вне офиса, и традиционное подключение к Интернету для вас недоступно. В таких ситуациях чувствуешь себя довольно беспомощным. По мере решения различных проблем, вы приходите к выводу, что сбои в работе конкретного сайта могут быть вызваны, как минимум, дюжиной причин. Это может быть отсутствие доступа к какому-либо внешнему источнику данных, например, сторонней базе данных или обработчику платежей, или проблемы с передачей файлов, так что для загрузки определенного фрагмента вашего Web-сайта используется неверная или устаревшая информация. Как бы то ни было, для того, чтобы идентифицировать причину сбоя вам необходимо получить несколько ключевых данных. Эти данные, или ключевые показатели производительности, могут быть разными для разных сайтов, тем не менее, всегда полезно иметь под рукой средство для получения подобной информации. Это и есть основная задача нашего приложения.

Приложение, которое мы рассмотрим в данной серии статей, должно помочь вам определить причины сбоя в работе Web-сайта, если вы вне офиса. Мобильное устройство на платформе iPhone или Android предоставляет вам широкие возможности для решения различных задач. Мы воспользуемся функциями этих мобильных платформ, чтобы облегчить задачу поддержки Web -сайта.


Конструктивные особенности

Основная цель, преследуемая при разработке нашего приложения – сделать его максимально независимым от серверных баз данных. Другими словами, мы хотим, чтобы наше приложение могло работать без использования учетных записей для доступа к сторонним сервисам. Пользователю потребуется просто скачать приложение и запустить его в локальном браузере. Конечно, пользователь может добавить закладку и время от времени обновлять страницу для загрузки новых улучшенных версий нашего приложения, однако вся необходимая для работы информация должны храниться в локальной базе данных SQL.

Безусловно, хранение данных на сервере имеет свои преимущества, однако рассмотрение функций, необходимых для создания и управления серверной базой данных, а также для обеспечения многопользовательского доступа к ней, не входят в задачи данной статьи. Наша цель – разработать практичное и полезное приложение, эффективно использующее локальное хранилище данных и возможности технологии Ajax. В заключении мы рассмотрим несколько дополнительных функций, реализация которых позволила бы расширить возможности приложения.

Код нашего приложения можно разделить на две части. Во-первых, это код, выполняемый на мобильном устройстве и включающий в себя:

  • Файл index.html, являющийся своего рода оболочкой нашего приложения.
  • Файл JavaScript netmon.js, реализующий большую часть функций нашего приложения.
  • Еще один файл JavaScript json2.js, содержащий необходимые подпрограммы для обмена данными в формате JSON.
  • Несколько файлов CSS, определяющих стили форматирования. Если вы знакомы с материалом Части 1, то вы знаете, что большая часть параметров форматирования содержится в основном CSS-файле. Кроме того, мы используем дополнительные файлы CSS для каждого устройства, позволяющие модифицировать внешний вид приложения в зависимости от конкретной платформы.
  • Библиотечный файл jquery.js для использования DOM и запросов Ajax.

Помимо этого, у нас есть код, исполняемый на сервере и уникальный для каждого из сайтов, которые мы хотим контролировать. Специфика этого кода определяется конкретной реализацией, но результирующий контент всегда один и тот же: объект JavaScript Object Notation (JSON) с определенным набором свойств и названий этих свойств, образующих пары «имя-значение». Кроме того, данные объекта JSON также определяют формат своего отображения в браузере в процессе работы нашего приложения. Кроме того, пары «имя-значение» являются индикаторами эффективности работы каждого конкретного сайта, которые (как мы надеемся) позволят быстро оценить приоритет и срочность существующих проблем, если таковые есть, и помочь службе поддержки обнаружить и устранить неисправности.


Разработка приложения

Начнем с рассмотрения модели данных нашего приложения. Поняв, как строится модель данных, мы сможем перейти к коду, который оперирует этими данными и обеспечивает работу приложения.

Модель данных

При сохранении информации в браузере мобильного устройства, данные хранятся в базе, определяемой стандартом HTML 5 или в SQL-хранилище, доступном из браузера. Спецификация SQL-хранилища браузера находится в процессе разработки, некоторые ее статьи пока не согласованы, однако с точки зрения практического применения SQL-хранилище вполне может использоваться в браузерах iPhone, Android и других основанных на WebKit браузерах. Подобная база данных представляет собой JavaScript-интерфейс к реализации хранилища SQLite. Наша база данных будет состоять из одной таблицы с именем tbl_resources.

Структура таблицы повторяет модель данных, используемую нашим приложением, а именно массив объектов JSON. Каждый объект содержит следующие данные:

  • Имя сайта.
  • URL-адрес домашней страницы сайта.
  • URL-адрес для получения ключевых данных о работе сайта; мы называем его ping URL.
  • Текущее состояние сайта: может быть OK или BAD.
  • Краткое описание текущего состояния сайта: короткое текстовое сообщение о проблемах, обнаруженных на сайте, например, сообщение о сбое работы базы данных.
  • Массив пар имя/значение, которые содержат специфические для конкретного сайта данные, позволяющие точнее оценить состояние сайта. Такие данные полезны и в случае, если никаких проблем в работе сайта пока не обнаружено: значения определенных показателей могут указывать на возможности возникновения нарушений в работе сайта в ближайшем будущем.

Листинг 1 демонстрирует пример объекта JSON, соответствующего одному сайту.

Листинг 1. Объект JSON, соответствующий одному сайту
    [
      {
         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'}
         ]
      }
   ]

Задача базы данных – обеспечить хранение полученной информации. При этом приложение в основном оперирует массивами объектов JSON вместо того, чтобы постоянно считывать данные, хранящиеся в базе. Подобная стратегия позволяет использовать упрощенный код JavaScript и минимизировать количество обращений к данным, хранящимся в базе. Однако в тех случаях, когда приложение должно обрабатывать большие объемы информации, удобнее обращаться непосредственно к базе данных или использовать своего рода «страничный» подход, при котором несколько элементов извлекаются из базы и используются в сочетании с массивом JavaScript, содержащем «окно» элементов, представляющих подмножество всех параметров.

На рисунке 1 показана структура базы данных и несколько текущих записей. Доступ к базе данных осуществляется с помощью программы Web Inspector, включенной в состав браузерной платформы Safari/WebKit. Теперь вы сами можете убедиться в эффективности использования WebKit для разработки Web-приложений. Программа Web Inspector работает на настольном компьютере, а приложение, использующее эту базу данных, будет работать на iPhone и Android.

Замечание: Приложение, рассматриваемое в этой статье, рассчитано на версию Android V2.0. Следует отметить, что набор функций, поддерживаемых движком WebKit, расширяется с каждым релизом.

Рисунок 1. Структура базы данных и несколько текущих записей
Рисунок 1. Структура базы данных и несколько текущих записей

Теперь, когда мы разобрались со структурой используемых данных, давайте рассмотрим, каким образом приложение работает с этими данными.


Работа на стороне клиента: вид по умолчанию

По умолчанию пользовательский интерфейс нашего приложения выглядит следующим образом: это список сайтов, за состоянием которых мы хотим следить, отсортированный по статусу сайта. Сайты, на которых обнаружены какие-то проблемы, показываются в начале списка.

Рисунок 2. Список проблемных сайтов
Рисунок 2. Список проблемных сайтов

Как показано на рисунке 2, приложение контролирует работу трех сайтов. В настоящий момент на двух из них обнаружены проблемы. В случае если сайт функционирует нормально, и его статус имеет значение ОК, краткое описание состояния сайта не показывается, а строка, соответствующая такому сайту, выводится черным шрифтом. Если статус сайта имеет значение BAD, то на экран выводится краткое описание проблемы. Кроме того, таблица стилей определяет стиль BAD для отображения данных о неблагополучном сайте, согласно которому все записи выводятся красным шрифтом. Более подробную информацию об используемых стилях вы можете найти в файле netmon.css. Полный код приложения предлагается в разделе Загрузка.

При нажатии на запись для какого-либо сайта, на экран либо выводится, либо наоборот, скрывается, информация о статусе этого сайта. Как показано на рисунке 2, для каждого сайта доступны три ссылки, после которых указываются URL-адрес домашней страницы и «ping URL» (адрес для получения обновленного статуса). Следом идет детальное описание состояния сайта, которое представляет собой список пар «имя-значение», определяющих текущий статус работы сайта.

Как мы видим, на сайте с названием ibm demo 2 обнаружена проблема «No more coffee?». Это, безусловно, не повод для срочного вызова специалистов технической поддержки, тем не менее, подобные забавные примеры тоже полезны в качестве демонстрации работы нашего приложения. Секция Details содержит более подробные данные о состоянии сервера, а именно «The Coffee Pot is empty».

Ссылка на домашнюю страницу сайта откроет ее в новом окне браузера. Текущие данные о состоянии сервера можно обновить, нажав на ссылку Refresh. Мы подробнее рассмотрим механизм обновления данных чуть позже.

Наконец, мы можем удалить запись для соответствующего сайта, нажав на ссылку Remove. Для подтверждения необратимого изменения используется простой запрос window.confirm().


Работа на стороне клиента - HTML

Для создания списка сайтов следует детально рассмотреть два файла. Первый из них – это файл index.html, код которого приведен в листинге 2, а второй – файл netmon.js, код которого показан в листинге 3. В текст статьи включены фрагменты кода, а полный код приложения вы можете найти в разделе Загрузка.

Мы по-прежнему используем поддерживаемый WebKit мета-тег viewport для того, чтобы указать браузеру, каким образом следует загружать страницу. Кроме того, мы используем JavaScript для загрузки определенного CSS-файла, соответствующего конкретному мобильному устройству.

Из новых элементов следует упомянуть добавление нескольких файлов JS и «локальных» файлов JavaScript. Кроме того, index.html теперь содержит новый HTML-тег div с ID entryform, который включает в себя элементы, необходимые для добавления новой записи непосредственно из приложения. Для добавления нового элемента в список серверов нам совсем не обязательно прибегать к традиционному подходу и загружать новую HTML-страницу. Вспомните, что одним из основных определенных нами конструктивных свойств приложения является отказ от использования дополнительных инструментов или баз данных на стороне сервера. В листинге 2 указаны операторы включения файла index.html.

Листинг 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" />');
   }

Работа на стороне клиента – формирование списка

Данные для формирования списка серверов извлекаются из нашей локальной базы данных и включаются в список серверов, отображаемый приложением. Для того чтобы получить такой список, мы должны открыть базу данных и выполнить SQL-запрос для извлечения всех элементов из таблицы.

Обычно в приложениях такого класса используется стандартный подход создания таблиц «на лету». Следует помнить, что у нас под рукой нет администратора БД, так что нашему приложению придется самостоятельно выполнять всю работу с базой данных. Листинг 3 демонстрирует код файла netmon.js, который выполняет несколько задач, в том числе:

  • Определение конструктора типа данных для объекта netmonResource.
  • Реализация группы функций, обеспечивающих взаимодействие приложения с базой данных и с Web-сайтами, состояние которых отслеживается приложением. Эта группа функций реализуется в пространстве имен netmon и включает в себя:
    • dbHandle— функция, обеспечивающая подключение к базе данных;
    • initialize— функция, реализующая создание/открытие базы данных и получение списка серверов для мониторинга. Код этой функции включен в листинг 3. Функция initialize открывает базу данных (если она еще не открыта) и выполняет запрос к базе. Если база данных не найдена, функция запускает процесс создания базы данных;
    • createResourcesTable— функция, которая создает требуемую базу данных и добавляет в нее одну запись для демонстрации;
    • addEntry— функция, которая служит для добавления новых записей в таблицу базы данных;
    • deleteEntry— функция, которая удаляет запись из таблицы базы данных;
    • refreshEntry— функция, которая обновляет запись, используя вызов Ajax и значение поля ping URL;
    • Resources— массив, содержащий «кэш» записей базы данных. Ряд функций нашего приложения работают с массивом Resources, вместо непосредственного обращения к базе данных;
    • Render— функция, возвращающая строку HTML, отформатированную согласно нашим параметрам вывода записи каждого сайта. Если мы захотим изменить формат вывода данных, эта функция наряду с соответствующими файлами CSS будет определять реализацию новых правил вывода.
Листинг 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>";
      }
   }
};

Пространство имен netmon и некоторые вспомогательные функции, определенные в файле index.html, реализуют основные возможности для работы со строкой состояния сайта. Рассмотрим теперь задачу добавления новой записи в базу данных.


Работа на стороне клиента – добавление нового сайта

Приложение использует список, содержащий один или несколько сайтов, чье состояние мы хотим отслеживать. При добавлении в список нового сайта, в базу данных заносится следующая информация:

  • Имя сайта;
  • URL-адрес домашней страницы сайта;
  • ping URL сайта.

Для выполнения этой задачи, мы создадим простую форму, которая включает в себя три поля с соответствующими именами и несколько кнопок (см. рисунок 3).

Рисунок 3. Простая форма для добавления нового сайта
Screen shows a simple form

Эта форма уже включена в файл index.html (см. листинг 2). Переключение между видом по умолчанию и формой для внесения новой записи осуществляется с помощью магических манипуляций jQuery. Список сайтов содержится в HTML-теге div с ID mainContent. Форма определяется в HTML-теге div с ID entryform, который по умолчанию скрыт. В любой момент времени отображается только один из этих двух тегов. Для переключения между ними используется jQuery-метод toggle, определенный для каждого из этих элементов (см. листинг 4).

Листинг 4. Функция addNewEntry
function addNewEntry() {
   $("#entry_name").val(");
   $("#entry_homeurl").val(");
   $("#entry_pingurl").val(");

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

После того, как пользователь ввел данные нового сайта, эта информация должна быть добавлена в базу данных, а общий список сайтов необходимо обновить для отображения новой записи. Эти операции выполняются JavaScript функцией saveEntry, включенной в файл index.html. Сама функция вызывается при нажатии пользователем на кнопку Save и создает новый объект JavaScript с именем netmonResource. Мы проверяем, что все поля содержат верные данные, а затем запускаем процесс добавления новой записи к базе данных. Функция netmon.addEntry сохраняет новую запись. После этого мы вызываем метод toggle для обратного переключения между двумя основными элементами div.

Замечание: обратите внимание, мы не используем регулярные выражения для проверки корректности URL-адресов нового сайта, хотя реализация подобной проверки была бы весьма полезна.

В ходе дальнейшего обсуждения рассматривается код netmon.js, приведенный в листинге 3.

Для организации доступа к базе данных SQL средствами JavaScript требуется выполнить два основных шага. Во-первых, базу данных нужно открыть, если она еще не открыта. В нашем примере для открытия базы данных используется функция initialize. Теперь рассмотрим код функции addEntry, которая используется для добавления новой записи (см. листинг 5). Все SQL-транзакции инициируются вызовом <databasehandle>.transaction(function (tx) {tx.executeSql()});. В нашем случае в качестве описателя базы данных database handle используется netmon.dbHandle.

Функция executeSql использует 4 аргумента:

  1. SQL-запрос, в котором все параметры, в том числе значения полей, представлены в виде ?. Каждый символ ? будет заменен на соответствующее значение, передаваемое во втором аргументе функции.
  2. Массив значений, которые будут подставлены вместо символов ? в SQL-запрос, указанный в качестве первого аргумента функции.
  3. Функция обратного вызова, выполняемая в случае, если SQL-запрос был успешно завершен. Аргументами этой функции являются функция обработки транзакции и набор результатов.
  4. Функция обратного вызова, выполняемая в случае, если SQL-запрос был завершен с ошибкой. Аргументами этой функции являются функция обработки транзакции и объект error.

Листинг 5 демонстрирует код функции netmon.addentry.

Листинг 5. Функция 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);
      }
   }

Теперь мы можем добавлять записи в базу данных. Удаление записей из списка выполняется аналогично. Операция удаления записей из базы данных реализована в функции netmon.deleteEntry.

Перейдем к рассмотрению механизма обновления данных с использованием технологии Ajax.


Работа на стороне клиента – обновление данных с помощью Ajax

Как мы уже упоминали, Ajax позволяет получить контент, генерируемый на стороне сервера, без полного обновления всей Web-страницы. Это достигается за счет использования различных технологий, определяемых конкретной версией браузера. В нашем случае мы используем библиотеку JavaScript jQuery, которая позволяет нам получить новые данные, не вдаваясь в детали реализации этой функциональности. Несмотря на то, что данная статья рассматривает вопросы разработки Web-приложений для браузеров iPhone and Android, мы считаем, что использование библиотеки JavaScript дает целый ряд преимуществ и должно всячески поощряться. Как только вы на своем опыте убедитесь в простоте и удобстве использования jQuery, мы уверены, что вы с нами согласитесь.

Функция netmon.refreshEntry, приведенная в листинге 6, содержит код Ajax и код для обновления записей в базе данных.

Листинг 6. Функция 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);
      }
      
   }

Для получения данных с сервера мы просто вызываем $.get(). В качестве первого аргумента передается URL-адрес, а в качестве второго – функция, которую следует выполнить по завершении вызова. В нашем примере мы заносим новые данные в базу (см. листинг 3, netmon.refreshEntry), используя функции работы с базой данных, которые рассматривались выше.

jQuery позволяет использовать данные в виде формы для передачи параметров Web-странице, а также позволяет указать, какого рода данные должны быть получены. В разделе Ресурсы вы можете найти полное описание Ajax-функций jQuery. В зависимости от функций, которые вы решите добавить в приложение, вам могут потребоваться дополнительные детали.

Обратите внимание на тот факт, что jQuery содержит метод, позволяющий получить непосредственно объект JSON. Тем не менее, мы остановили свой выбор на использовании «стандартной» функции Ajax с тем, чтобы вы в дальнейшем могли использовать наше приложение в качестве основы для разработки других программ.

При получении данных от сервера нам нужно преобразовать их в объект JavaScript, чтобы получить доступ к его свойствам. Это выполняется при помощи функции JavaScript eval: var object = eval(<json text>);.

На этом мы заканчиваем рассмотрение основных функций на стороне клиента. Теперь осталось разобраться с последним этапом, а именно со страницами на стороне сервера, которые отвечают за генерацию объектов JSON, используемых нашим приложением.


Работа на стороне сервера – создание страницы с данными сервера

Мы описали структуру объекта JSON, получаемого нашим приложением от каждого сервера, но как происходит генерация этих объектов? Короткий ответ звучит так: это определяете вы сами. Если вы предпочитаете использовать .NET-технологии, вы можете создать страницу .aspx для генерации необходимых данных. В качестве альтернативы вы можете воспользоваться shell-скриптом, который автоматически выполняется на сервере через определенные промежутки времени и создает текстовый файл, содержащий последние данные о состоянии этого сервера. Приложению важно получить объект JSON, соответствующий определенному URL-адресу, но каким образом этот объект будет создаваться – решать вам.

В листинге 7 приводится пример PHP-кода, который создает список файлов и проверяет, нет ли в списке файла с именем error.txt. Если такой файл есть, и он не пуст, то мы считаем, что на сервере есть какая-то проблема и создаем объект JSON с указанием статуса BAD и содержимым файла error.txt в качестве краткого описания этой проблемы.

Листинг 7. Пример PHP-кода
<?
$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) ?>"
}
]

Результатом выполнения кода, приведенного в листинге 7, будет объект JSON, показанный в листинге 8 (при этом предполагается, что на сервере найден файл error.txt).

Листинг 8. Объект JSON, сгенерированный в результате выполнения кода листинга 7
[
    {
        "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." 
    } 
]

В этом примере мы указываем время создания объекта, количество найденных файлов и список этих файлов, содержащий имя файла и его размер. При этом следует учитывать, что сервер может находиться в другом временном поясе, нежели вы.

Поскольку файл error.txt ненулевого размера, мы считываем его содержание и записываем его в качестве свойства summary. Вот и все – простейший скрипт мониторинга состояния сервера готов к работе.

В процессе работы по созданию объектов JSON вы можете столкнуться с определенными трудностями при разборе кода на стороне приложения. В этом случае имеет смысл воспользоваться средствами проверки корректности JSON кода (см. раздел Ресурсы). На рисунке 4 показано, как выглядит запись для сервера, статус которого мы только что разобрали, в окне настольного браузера.

Рисунок 4. Использование средств проверки корректности JSON
Рисунок 4. Использование средств проверки корректности JSON

Рисунок 5 демонстрирует, как выглядит наше приложение в браузере iPhone.

Рисунок 5. Приложение, работающее в браузере iPhone.
Рисунок 5. Приложение, работающее в браузере iPhone.

Рисунок 6 демонстрирует наше приложение в браузере Android с использованием измененного списка серверов.

Рисунок 6. Приложение, работающее в браузере Android
Рисунок 6. Приложение, работающее в браузере Android

На этом работа над нашим приложением может считаться завершенной. Безусловно, всегда найдутся новые опции или свойства, которые можно было бы добавить к существующей реализации программы, так что в следующей (заключительной) части мы рассмотрим список функций, которые вы можете реализовать самостоятельно в качестве упражнения.


Следующие шаги

При обсуждении дополнительных модификаций, позволяющих улучшить работу приложения, как правило, недостатка в новых идеях не бывает. Вот лишь несколько функции, которые было бы полезно добавить:

  • Редактировать существующие записи – сейчас вы можете только добавлять новые или удалять существующие записи.
  • Использовать регулярные выражения для более эффективной проверки правильности заполнения полей формы.
  • Добавить возможность отправки электронного письма коллегам непосредственно из приложения.
  • Сохранять свои настройки на сервере, чтобы вы могли использовать различные мобильные устройства.
  • Преобразовать данное приложение в «родное» приложение средствами PhoneGap или Appcelerator. Это позволит вам выставить свое приложение на продажу в AppStore.

Заключение

Эта статья опирается на базовый подход к разработке многофункциональных мобильных Web-приложений, рассмотренный в Части 1, и продолжает обсуждение вопросов создания Web-приложений с использованием локального SQL-хранилища и запросов Ajax. Добавив небольшие программы, работающие на стороне серверов, вы можете использовать приложение, рассматриваемое в этой статье, для мониторинга состояния сети.

Надеемся, что пример, предлагаемый в данной статье, воодушевит вас на разработку собственного мобильного приложения для iPhone или Android.


Загрузка

ОписаниеИмяРазмер
Part 2 source codeos-androidiphone2-browserwars2.zip31KB

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=555276
ArticleTitle=Android и iPhone - войны браузеров: Часть 2. Разработка приложения для браузера на платформе iPhone или Android
publish-date=10202010