Текст книги "Программирование мобильных устройств на платформе .NET Compact Framework"
Автор книги: Иво Салмре
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 52 (всего у книги 69 страниц)
Точно так же как группа людей, действующих согласованно, может достигнуть гораздо большего, чем один человек, действующий в одиночку, возможности мобильного приложения, взаимодействующего с окружающими его сетями, намного превышают возможности изолированного приложения. Чтобы группа людей в некоторой организации могла успешно справляться с поставленными задачами, должны быть предусмотрены меры, позволяющие корректно разрешать ситуации, соответствующие перебоям в связи между членами группы и перебоям в работе каждого из них. Ни одно звено взаимодействия не должно быть критическим, иначе при его сбое наступит сбой в работе организации в целом. То же самое справедливо и в случае мобильных устройств.
Мобильные устройства значительно выигрывают от взаимодействия с внешним миром, но они никогда не должны всецело зависеть от такого рода взаимодействий. Несмотря на всю очевидность этой истины, ею часто пренебрегают.
Каждая удачная попытка доступа к сетевым ресурсам должна рассматриваться в вашем приложении скорее как счастливая случайность, а не как закономерная необходимость. Для большинства мобильных устройств сеансы взаимодействия с сетью осуществляются "почти по требованию" в том смысле, что хотя пользователь мобильного приложения, как правило, и может перейти в то физическое место, где сеть является доступной, но он не может рассчитывать на то, что устройство сможет оставаться постоянно подключенным к сети. В этом отношении мобильные устройства значительно отличаются от настольных компьютеров, имеющих постоянное кабельное подключение к сети, и лэптопов, подключение которых к сети в условиях стационарной работы не составляет большого труда. Если увидеть человека, склонившегося над клавиатурой своего лэптопа в зале ожидания железнодорожного вокзала, можно не так уж часто, то людей, манипулирующих клавишами своих мобильных телефонов, на том же вокзале встретится довольно много. И хотя количественные оценки этих показателей могут колебаться в зависимости от обстоятельств, различия между обеими ситуациями налицо.
Следствием подхода, в соответствии с которым сетевой доступ необходимо рассматривать лишь как нерегулярный, являются две полезные рекомендации, касающиеся проектирования мобильных приложений:
1. Полезные данные необходимо загружать и кэшировать заблаговременно, когда доступно сетевое соединение. Очень важно определить, какие виды информации будут наиболее полезными для вашего приложения при его работе в автономном режиме, и сделать все для того, чтобы соответствующие данные были заблаговременно загружены и доступны, когда в них возникнет потребность. В то время как при работе с локальными ресурсами устройства целесообразно откладывать их размещение в памяти до тех пор, пока они не потребуются, информацию, хранящуюся вне устройства, целесообразно заранее загружать и накапливать, чтобы подготовиться к работе приложения в автономном режиме. Например, подключение к локальной базе данных устройства может быть отложено до момента, когда в этом возникнет действительная необходимость. В отличие от этого, нужные данные следует загружать из внешней базы данных заблаговременно, когда будет доступно соединение с сетью. В идеальном случае загрузка данных должна выполняться как фоновая задача, которая не блокирует пользовательский интерфейс. Кроме того, предварительная загрузка информации позволяет существенно повысить производительность приложения, особенно в тех ситуациях, когда приложению или пользователю эти данные требуются часто, но иначе их пришлось бы каждый раз загружать по требованию.
2. Организуйте очередь данных, подлежащих выгрузке, и выгружайте их, как только становится доступным сетевое соединение. При работе с локальными приложениями пользователи устройства хотят ощущать их обратную реакцию. Если для обновления данных приложению, выполняющемуся на мобильном телефоне, требуется установить соединение с сервером, то это может сопровождаться определенной задержкой, и пользователь будет чувствовать себя неуверенно. Гораздо лучше организовать надежную очередизацию обновлений на клиенте и предложить пользователю пакетную выгрузку обновляемых данных на сервер, осуществляемую автоматически или вручную в те периоды времени, когда это становится возможным. Код, с помощью которого выполняется выгрузка данных, также должен быть достаточно отказоустойчивым, чтобы в случае невозможности установления надежного соединения с сервером эту работу можно было отложить на более позднее время.
Не допускайте того, чтобы поток пользовательского интерфейса блокировался на длительное времяДва примера коммуникационных стратегий
Синхронизация данных расписания встреч, списка контактов и электронной почты, хранящихся на мобильном устройстве
Хорошим примером применения двух описанных выше принципов является модель синхронизации, используемая Microsoft на устройствах Smartphone и Pocket PC Phone. На устройствах обоих типов имеется приложение электронной почты, допускающее периодическую синхронизацию электронной почты, данных расписания встреч (календаря) и списка контактов с сервером обмена сообщениями Exchange Server.
Запросы и предпочтения различных пользователей в отношении синхронизации этих данных различны. С целью минимизации потока данных мобильные приложения предоставляют пользователям возможность задавать, какие виды данных подлежат локальной синхронизации на устройстве. Например, я могу решить, что мне необходима синхронизация только расписания встреч и списка контактов, оставляя задачу синхронизации электронной почты для своего лэптопа. Можно также ограничивать объем синхронизируемой информации, указав, например, что данные обо всех предстоящих встречах должны синхронизироваться полностью, тогда как синхронизация данных о прошлых встречах должна охватывать информацию не более чем 2-недельной давности, а при загрузке электронных сообщений должны загружаться только первые 3 Кбайт данных.
Развернутое на устройстве приложение для работы с электронной почтой, информацией о контактах и расписанием встреч позволяет также указывать, с какой периодичностью и в каком режиме должна осуществляться синхронизация – автоматически или вручную. Наиболее подходящие для каждого пользователя установки определяются видом синхронизируемых данных и потребностями в их обновлении. У большинства пользователей расписание встреч изменяется реже, чем появляются новые входящие электронные сообщения, а список контактов изменяется еще реже. Лично я предпочитаю синхронизировать данные во время поездок на работу и с работы и, если это необходимо, на протяжении рабочего дня; другие люди, насколько мне известно, предпочитают синхронизировать свои данные чаще, используя автоматическое обновление. Модель синхронизации в значительной степени определяется тем, какой режим использования приложения является для пользователя привычным.
Для синхронизации этой информации устройства используют специальную службу, которая называется ActiveSync. Механизм ActiveSync периодически устанавливает через мобильную сеть соединение с Internet для подключения пользователя к серверу Exchange Server и последующей синхронизации данных. Процесс синхронизации состоит из двух операций:
1. Загрузка всех пакетных обновлений. Синхронизируются все локальные изменения, связанные с расписанием встреч, электронной почтой и списком контактов.
2. Загрузка данных, удовлетворяющих условиям локального фильтра. Соответствующим образом загружается информация о новых встречах и контактных лицах, а также заголовки электронной почты.
Эта модель позволяет сохранять разумный баланс между объемом обновляемых данных и пропускной способностью сети. Если бы информация о назначенных встречах загружалась по запросу индивидуально для каждой даты при ее выборе пользователем в календаре своего мобильного устройства, то во многих случаях сделать это было бы невозможно из-за отсутствия соединения с мобильной сетью или выключения сервера электронной почты. Но даже и при наличии соединения пользователь испытывал бы значительные неудобства от задержек при доступе к данным или их выгрузке. С другой стороны, если бы во время синхронизации с сервера загружалась вся возможная информация, то это происходило бы слишком медленно и неэффективно с точки зрения использования полосы пропускания. Совершенно очевидно, что достижение необходимого баланса указанных факторов требует вмешательства пользователя
Хорошо продуманное кэширование данных на устройстве, разумное их обновление в соответствии с потребностями пользователя и предоставление пользователю возможности самостоятельно определять, когда и какие именно данные должны синхронизироваться, создает все предпосылки для нормальной работы, не сопровождающейся неожиданными сбоями.
Отправка и получение SMS-сообщений с помощью мобильного телефона
SMS-сообщения – это короткие текстовые сообщения, пересылаемые между мобильными телефонами. SMS – многообещающая технология, используя которую пользователи мобильных телефонов могут легко обмениваться между собой несколькими строками текста. Устанавливая фиксированную плату за пересылку одного сообщения, операторы сетей мобильной связи получают огромные прибыли за счет предоставления лишь узкой полосы пропускания; SMS-сообщения с побитовой оплатой гораздо прибыльнее речевой связи! Таким образом, приложения для обработки SMS – это приложения-приманки, и вряд ли можно сомневаться в том, что изготовители мобильных телефонов и операторы сетей мобильной связи постарались отладить эту технологию идеальным образом.
Обмен SMS-сообщениями обладает одним большим достоинством и одним большим недостатком:
1. Преимущества SMS: разделение передачи и приема сообщений. Если SMS-сообщение пересылается с одного мобильного телефона на другой, то телефон, принимающий сообщение, во время его передачи не обязан быть подключенным к сети. Если вы совершаете перелет через Атлантику, и в этот момент кто-то отправляет вам SMS-сообщение, инфраструктура сети мобильной связи поместит сообщение в очередь на своих серверах и доставит его вам, когда вы приземлитесь и включите свои мобильный телефон. Точно так же будут развиваться события и в том случае, если принимающий телефон находится вне зоны досягаемости поскольку его владелец в данный момент находится в метро, лифте или катается на лыжах на той части склона, которая скрыта от сотового передатчика. По указанным причинам данный механизм прекрасно подходит для отправки кратких текстовых сообщений наподобие: "Встретимся в Лонгхорн Грилл в 17:00. Иво". Отправитель и получатель могут общаться между собой краткими предложениями, не соединяясь друг с другом.
2. Недостаток SMS: немые клиентские приложения, не имеющие возможности кэшировать сообщения в автономном режиме. В настоящее время большинство мобильных приложений не имеют автоматизированного механизма очередизации исходящих SMS-сообщений. Когда пользователь набирает сообщение и пытается его отправить, делая это обычно несколько раз, то при отсутствии доступа к сети сообщение не может быть отправлено. Эта ситуация служит примером модели синхронного приложения, в котором попытка соединения осуществляется немедленно и завершается либо удачно, либо неудачно, прежде чем управление возвращается пользователю. Гораздо более эффективной является модель клиентского приложения в соответствии с которой предпринимается попытка синхронной отправки сообщения, и если сделать это немедленно не удается, то сообщение помещается в очередь исходящих сообщений, отсылка которых будет произведена тогда, когда доступ к сети будет восстановлен. Когда SMS-сообщение, в конце концов, поступит на сервер, пользователь, отправивший сообщение, может быть извещен об этом по телефону.
Как и в предыдущем примере, отсюда можно извлечь важные уроки. Вы не задумывались над тем, почему в современных мобильных телефонах на клиентской стороне SMS-технологии действует подобное ограничение? Объясняется это тем, что создать асинхронную коммуникационную модель значительно сложнее; это требует написания для клиентской стороны логики, которая будет осуществлять фоновую отправку SMS– сообщений, обрабатывать повторные попытки отправки сообщений и формировать очередь исходящих сообщений, а в довершение всего неплохо было бы также предоставить пользовательский интерфейс, который после окончательной отправки SMS– сообщений, помещенных в очередь, известит об этом пользователя. Модели приложений, используемые предыдущими поколениями мобильных телефонов, отличались сравнительной простотой, и создание подобного рода систем считалось неоправданным, ибо вносило дополнительные сложности. Однако современные мобильные телефоны располагают как более мощными пользовательскими интерфейсами, так и моделями приложений, облегчающими выполнение фоновой обработки. Поэтому разумно предположить, что операционные системы и приложения будущих поколений мобильных телефонов будут поддерживать кэширование исходящих SMS-сообщений точно так же, как многие нынешние телефоны поддерживают синхронизацию с сервером кэшируемых исходящих сообщений электронной почты, контактной информации и расписания встреч.
По самой своей природе взаимодействие является синхронной операцией; ваше мобильное приложение должно передать некоторый блок данных на сервер или принять блок данных с сервера, причем для перемещения этого блока данных требуется некоторое время. Поскольку длительность прохождения данных до сервера, настольного компьютера или иного внешнего устройства ваше приложение непосредственно контролировать не может, важно не выполнять такого рода операции в том же потоке, в котором выполняется пользовательский интерфейс вашего приложения.
Разработчики приложений очень часто допускают ошибку, суть которой состоит в том, что для всех создаваемых коммуникационных систем сначала предусматривается их выполнение потоком пользовательского интерфейса в синхронном режиме, которое планируется заменить на более поздних стадиях разработки приложения выполнением коммуникационных задач фоновыми потоками. Как правило, такой подход не приводит к удовлетворительным результатам. Существует несколько причин, по которым разработчики попадают в эту ловушку:
■ Синхронные взаимодействия проще проектировать и отлаживать. Это истинная правда. Проектировать и отлаживать логику приложения, которая выполняется в синхронном режиме, значительно проще. По этой причине действительно рекомендуется, чтобы вы проектировали и отлаживали коммуникационную логику, выполняя ее синхронно с логикой пользовательского интерфейса вашего приложения. Коммуникационные процедуры, которые вы пишете, должны быть синхронными функциями. Однако, как только эти коммуникационные функции написаны, и их основная функциональность прошла тестирование, необходимо немедленно организовать их асинхронное по отношению к логике пользовательского интерфейса выполнение.
■ Синхронный код пишется гораздо быстрее. И это правда. Когда перед вами маячат сроки контрольного этапа, очень легко убедить себя в том, что единственный способ закончить работу вовремя – это срезать углы, организовав выполнение коммуникационной логики синхронно с выполнением логики пользовательского интерфейса.
■ Коль скоро мы не забываем о необходимости осуществления коммуникаций в асинхронном режиме и предусматриваем это в проекте приложения, то организовать впоследствии обмен данными в асинхронном режиме не составит никакого труда. Это – заблуждение. Несомненно, если вы не забываете о том, что, в конечном счете, коммуникационные операции должны будут выполняться асинхронно, то это облегчит вам переориентирование написанных вами функций синхронной связи на асинхронный режим выполнения в будущем, но одного этого еще мало. Истина состоит в том, что, как бы вы ни старались это предотвратить, в код, использующий синхронные процедуры, будут "намертво" встроены синхронные зависимости. Человек просто не в состоянии уследить за всеми неявными допущениями, которые вплетаются в логику приложения, и устранить возникающие из-за этого проблемы на более поздних этапах разработки приложения вам будет очень трудно.
Наилучшая методология проектирования коммуникационного кода, который должен обеспечивать постоянную способность пользовательского интерфейса к отклику, заключается в том, чтобы следовать приведенным ниже четырем рекомендациям:
1. Проектируйте свои коммуникационные процедуры в виде отдельных, надежно инкапсулированных функций. Лучший способ подготовить коммуникационные процедуры к асинхронному выполнению – это надежно их инкапсулировать. Коммуникационная процедура, которая пересылает данные на сервер, должна передавать статическую копию этих данных; для получения этих данных функции не должен требоваться доступ к каким-либо глобальным либо разделяемым состояниям. Аналогичным образом, коммуникационная процедура, осуществляющая считывание данных с сервера, не должна изменять никакое глобальное состояние приложения, пока не прочитает все данные. Очень важно соблюдать эти принципы, поскольку одновременная работа двух потоков с одним и тем же глобальным состоянием приложения – это верный путь к дополнительным сложностям, разрушению данных и снижению надежности приложения. Поток пользовательского интерфейса должен иметь исключительный доступ к данным, с которыми он работает, коммуникационным процедурам должна предоставляться копия этих данных, а взаимодействие между обеими системами должно осуществляться лишь в строго определенных и надежно протестированных точках.
2. Тестируйте коммуникационные процедуры, вызывая их в синхронном режиме. Как уже отмечалось ранее, тестировать и отлаживать коммуникационный код намного проще, если он выполняется в синхронном режиме потоком пользовательского интерфейса. По этой причине гораздо целесообразнее сначала выявить все ошибки в коде, выполняющемся синхронно с пользовательским интерфейсом, и лишь после этого переходить к тестированию кода при его выполнении фоновым потоком. Я рекомендую вам помещать на форму большую кнопку с надписью "Тестировать и сохранить код" и использовать ее для тестирования и отладки своих коммуникационных процедур. Причина, по которой я рекомендую использовать именно "большую кнопку", заключается в том, что такая кнопка выглядит уродливо и бросается в глаза, так что вы никогда не забудете удалить ее.
3. Ужесточайте условия тестирования своих коммуникационных процедур, вызывая их в асинхронном режиме в намеренно затрудненных ситуациях, используя тестовое приложение. Код, выполняемый фоновым потоком, всегда оказывается сложнее синхронно вызываемого кода. Кроме того, отслеживать все тонкие разновидности повреждения данных и логические ошибки в управлении состоянием, возникающие в условиях, когда несколько различных потоков взаимодействуют между собой самым непредсказуемым образом, весьма нелегко, что дополнительно осложняется наличием многофункционального кода приложения, который окружает отлаживаемый код. Лучший способ создания действительно надежных систем – это жесткое тестирование кода в упрощенном окружении, предназначенном для помещения кода в специально осложненные состояния, и мониторинг выполнения кода для выявления необычных ситуаций. Целесообразно создать специальное приложение, в котором несколько потоков кода выполняются асинхронно, и тщательно исследовать внутреннее состояние приложения для выявления любых возможных неожиданных результатов. Тестирование такого рода может указать вам на необходимость введения некоторых ограничений в выполняемый код, которые не позволят приложению переходить в состояния повышенного риска; так, код, предназначенный для выполнения фоновой задачи, может активно препятствовать выполнению нескольких экземпляров этой задачи параллельными потоками, если вы обнаружили, что надежность такого выполнения является проблематичной, а использование многопоточности не является обязательным. Тестированный, усовершенствованный и отлаженный подобным образом код можно включать в код приложения с гораздо большей уверенностью, нежели код, который считается заведомо корректным исключительно на основании голого оптимизма.
4. Проведя тестирование кода, немедленно преобразуйте коммуникационные процедуры, чтобы они могли выполняться в рабочем фоновом потоке вашего приложения. После отладки базового коммуникационного кода в синхронном режиме и его тестирования в асинхронном режиме он будет готов к помещению в асинхронную операцию, выполняемую в вашем приложении. Приложение должно основываться на понятной и согласованной модели выполнения кода рабочим фоновым потоком, и эту же модель следует применять для всех ваших асинхронных коммуникационных потребностей. Эту модель асинхронного выполнения всегда необходимо использовать и при встраивании коммуникационного кода в пользовательский интерфейс приложения. Не встраивайте синхронные вызовы коммуникационных процедур в пользовательский интерфейс, планируя впоследствии, когда у вас появится время, преобразовать их для выполнения в асинхронном режиме; чем больше кода вы сюда поместите, тем больше зависимостей будет создано.
Как правило, гораздо проще взять в качестве исходной асинхронную систему и выполнять ее в синхронном режиме, чем поступать наоборот. Чтобы синхронно выполнить асинхронную систему, ваше приложение должно просто вызвать асинхронный код и начать выполнять цикл ожидания, выход из которого и выполнение дальнейшего кода осуществляются после завершения выполнения асинхронного кода. Чтобы превратить синхронное взаимодействие в асинхронное, вы должны идентифицировать все состояния приложения, которые затрагиваются коммуникационными процедурами, и создать отдельные копии этих данных, вы должны создать модель для фонового выполнения, из которой будет вызываться код, вы должны перепроектировать способ, используемый коммуникационным кодом для взаимодействия с элементами пользовательского интерфейса, поскольку организовать надежный доступ к элементам управления пользовательского интерфейса из других потоков во многих случаях невозможно (эта ошибка, несомненно снижающая надежность приложения, является весьма распространенной при использовании .NET Compact Framework! Для организации взаимодействия с элементами управления пользовательского интерфейса из других потоков следует использовать вызов метода <ЭлементУправления>.Invoke()), вы должны разработать способ уведомления пользовательского интерфейса о возникновении проблем в процессе информационного обмена, вы должны предусмотреть корректную обработку сбойных ситуаций и, наконец, вы должны разработать модель для обмена командами и состояниями между пользовательским интерфейсом и коммуникационным кодом. Сделать все это уже после того, как разработка приложения почти закончена, очень трудно, и любые попытки преобразования синхронных операций в асинхронные в мобильном приложении потребуют, по крайней мере, существенных переделок и, как правило, привнесут в ваше приложение логические ошибки и сделают его работу ненадежной. С самого начала проектируя код, с помощью которого осуществляется информационный обмен с внешними источниками информации, в асинхронной форме, вы получите намного более удовлетворительные результаты.
НА ЗАМЕТКУ
Выполнение кода в фоновом потоке обсуждается в главе 9.







