Текст книги "Программирование мобильных устройств на платформе .NET Compact Framework"
Автор книги: Иво Салмре
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 12 (всего у книги 69 страниц)
Идентифицировав ключевые сценарии и возможности мобильного приложения, вы должны позаботиться о создании наилучших условий работы для тех, кто будет им пользоваться. Это означает, что в процессе проектирования и создания кода мобильного приложения вы должны оптимизировать его производительность и интерактивные свойства.
■ Вы должны определить общие показатели, характеризующие способность вашего приложения реагировать на действия пользователя. Например: "На появление диалога не должно уходить более 0,5 секунды", "Визуально определяемая реакция на нажатие клавиши должна следовать немедленно" или "Раскрытие узлов дерева не должно занимать более 0,15 секунды". Здесь были приведены в качестве примера неплохие значения показателей, к достижению которых следует стремиться.
■ Вы должны определить конкретные числовые показатели для ключевых сценариев. Например: "Загрузка данных инвентаризационной ведомости ни при каких обстоятельствах не должна останавливать работу пользовательского интерфейса на более чем 0,2 секунды без появления курсора ожидания" или "Дли тельность загрузки данных инвентаризационной ведомости ни в коем случае не должна превышать 3 секунд".
Тестируйте свои допущения в реальных условиях эксплуатации сценариев. Даже если в вашем распоряжении еще не имеются все необходимые реальные данные или код, легко тестировать эффекты задержек при выполнении пользователем часто встречающихся задач, имитируя их путем вставки состояний ожидания в скелет кода пользовательского интерфейса приложения. Очень важно хорошо себе представлять, какие периоды ожидания приемлемы, а какие – нет. Следует стремиться к тому, чтобы приложение всегда предоставляло немедленное подтверждение того, что та или иная операция находится в состоянии выполнения, если она не может быть завершена очень быстро. Кроме того, если работа должна выполняться в течение длительного или неопределенного времени, ее выполнение может быть поручено фоновому потоку.
Разрабатывая приложение, используйте реалистические размеры данных, которые либо соответствуют предполагаемым размерам данных, с которыми придется работать пользователю, либо превышают их. Одной из распространенных ошибок, допускаемых разработчиками в процессе создания приложений, является использование тестовых данных небольшого объема, и иногда это приводит к тому, что при работе с реальными объемами фактических данных приложение функционирует из рук вон плохо. Чем раньше вы начнете тестировать приложение на реальных данных, тем раньше эти проблемы всплывут на поверхность и тем выше вероятность того, что вам удастся справиться с проблемами производительности
Блок-схема организации процесса разработки мобильного приложения представлена на рис. 4.1. Если вы столкнулись с проблемами производительности – остановитесь! Можно сформулировать это и по-другому: если в процессе проектирования и разработки приложения вы столкнулись с проблемами производительности – немедленно прекратите дальнейшее написание кода! Слишком уж часто разработчики с головой погружаются в это занятие, стремясь поскорее получить "завершенный код" и давая себе слово, что к решению возникших проблем производительности они после этого обязательно вернутся. В лучшем случае такая стратегия является рискованной! Она редко приводит к успеху при разработке приложений для настольных компьютеров и серверов и обычно дает довольно-таки плачевные результаты; в случае же мобильных устройств результаты ее применения будут еще худшими. Причина этого заключается в том, что проблемы производительности часто коренятся не в недостатках какого-то отдельного алгоритма, который можно просто доработать или оптимизировать, не влияя на остальные части системы. До тех пор пока вы детально не выясните природу проблемы и не убедитесь в том, что альтернативное решение способно ее устранить, вы не можете сказать фактически ничего определенного о том объеме переработки проекта, который для этого может потребоваться. Вам может повезти, однако везение довольно быстро покидает тех программистов, которые слишком сильно полагаются на это.

Рис. 4.1. Методология, подчиненная требованиям производительности
Во многих случаях проблемы производительности носят систематический характер и касаются всего приложения в целом. Систематическое снижение производительности может быть, в частности, обусловлено способом обмена данными с памятью, количество одновременно удерживаемых в памяти ресурсов, а также способом перерисовки и обновления информации на экране, который используется пользовательским интерфейсом. Проблемы производительности подобного рода обычно появляются из-за неудачного выбора основных проектных решений, и, чтобы избежать их, необходимо позаботиться об этом уже на самых ранних стадиях процесса проектирования и разработки приложения. Потери от чрезмерно быстрого продвижения вперед для получения "завершенного кода" и последующего возврата к пересмотру стратегии доступа к данным, повторному проектированию модели использования памяти и организации выполнения определенной работы в фоновом режиме для улучшения интерактивности пользовательского интерфейса могут быть поистине огромными. Даже если в результате внесения запоздалых проектных изменений вы и получите работающий код, он будет плохо организован. Реальные основы высокой производительности приложения должны закладываться еще в его фундаменте.
Не забывайте также о том, что по мере разработки приложение будет постепенно разрастаться и усложняться. Вы будете писать все больше кода, создавать все новые объекты, и у вас будет появляться все больше компонентов, конкурирующих между собой за право обладания ограниченным пулом ресурсов. Если вы столкнетесь с проблемами производительности приложения и не займетесь ими сразу же, как только они замечены, то по мере того, как объем кода будет расти, эти проблемы будут только усугубляться, а не ослабляться. К тому же добавление нового кода будет порождать дополнительные зависимости в моделях данных, памяти, пользовательского интерфейса, коммуникационной модели и других логических частях программы, которые вы написали. Откладывая решение проблем "на потом", вы сами себя загоняете в угол.
Нередко разработчики рассуждают таким, на первый взгляд вполне разумным, но на самом деле глубоко ошибочным образом: "Понять, какие части приложения нуждаются в улучшении, можно лишь только после того, как будет написан весь код". Такая позиция весьма порочна, поскольку в ней содержится предположение о том, что отдельные части вашего приложения каким-то удивительным образом не зависят друг от друга. Вместе с тем, когда вы напишете весь код, в него будет введено множество явных и неявных зависимостей между различными системами, и вы, вероятнее всего, будете в состоянии внести в код лишь некоторые изменения количественного, но не качественного характера. Вам не уйти от этого даже в том случае, если вы приложите максимум усилий к инкапсуляции кода приложения. Чем больше объем кода, тем больше в нем существует зависимостей между отдельными частями; пытайтесь бороться с этим, тщательно продумывая структуру приложения, но знайте, что таковы реалии жизни. Браться за решение проблем производительности следует тогда, когда базовый код еще обладает достаточной гибкостью и остается открытым для реструктуризации. Лучше всего заниматься этим сразу же по ходу написания кода.
Как только у вас возникнут проблемы с производительностью – остановитесь, оцените ситуацию и выясните, что именно происходит. Не связано ли это с неэффективным обновлением пользовательского интерфейса? Не хранится ли в памяти слишком много данных, в результате чего "сборщику мусора" приходится непрестанно трудиться? Не является ли данный вид обработки длительным изначально длительным в силу своих особенностей, в результате чего такую обработку целесообразнее выполнять в фоновом режиме, асинхронно по отношению к пользовательскому интерфейсу? Не работаете ли вы с большими объемами данных, используя высокоуровневую объектную модель с сохранением состояний, тогда как лучше было бы использовать низкоуровневую программную модель без сохранения состояний? Определите, в чем коренятся причины проблемы, и соответствующим образом перестройте структуру кода для их устранения, прежде чем далее создавать код, который будет зависеть от принятых вами допущений.
Придерживаясь в процессе разработки приложения определенной дисциплины и не забывая о производительности, вы сможете добиться того, что код, а также модели использования данных и памяти вашего приложения не будут содержать ничего лишнего, а дизайн пользовательского интерфейса будет отличаться ясностью и эффективностью.
Высокая производительность – основная предпосылка создания удобных условий работы для пользователя
(если производительность низка, выполнение последующих шагов вам ничего не даст)
Если вы создадите привлекательный и интуитивно понятный интерфейс, простую и надежную коммуникационную модель и объектно-ориентированную модель данных, но ваше приложение будет вести себя неудачным с точки зрения конечных пользователем образом, то они будут разочарованы. В силу самой природы мобильных устройств конечные пользователи рассчитывают на их высокую интерактивность. Люди всегда носят эти устройства при себе и надеются, что смогут воспользоваться ими сразу же, как только в этом возникнет необходимость.
Очень важно понимать, что восприятие любых эксплуатационных параметров приложения носит исключительно субъективный характер. Пользователя не интересует, сколько времени ушло на получение данных, ему важно лишь то, насколько длительным показался ему этот промежуток, и что в это время происходило. Это обстоятельство может быть выгодно использовано при проектировании приложения. Длительно выполняющиеся операции не будут казаться большим злом, если поведение приложения таково, что у пользователей складывается впечатление, будто они сохраняют контроль над ситуацией и ощущают ответную реакцию приложения. Главные доказательства высокой производительности приложения заключены в том, каким его воспринимает реальный пользователя.
Поставьте перед собой формирование нужного вам пользовательского восприятия производительности приложения в качестве основной цели. Эту задачу вы должны начать решать еще до того, как приступите к проектированию пользовательского интерфейса, многие аспекты которого будут определяться требованиями производительности. Например, предположим, что в приложении предусмотрена запрашиваемая пользователем операция, для выполнения которой требуется 20 секунд. Поскольку оставить пользовательский интерфейс без возможности реагировать на действия пользователя на целых 20 секунд было бы недопустимо, эта работа должна выполняться в фоновом режиме, чтобы в процессе обработки запроса пользовательский интерфейс сохранял свою интерактивность. Если вы поступите именно так, то часть визуального пространства, занимаемого пользовательским интерфейсом, целесообразно выделить для вывода информации о состоянии выполнения данной операции, чтобы пользователь знал, что его запрос принят и обрабатывается.
Шаг 2: спроектируйте подходящий пользовательский интерфейсПример разработки пользовательского интерфейса с учетом обеспечения высокой производительности
Как ActiveSync, так и Internet Explorer для Pocket PC и интеллектуальных телефонов предлагают элементы пользовательского интерфейса, назначение которых состоит в том, чтобы информировать пользователей о состоянии выполнения инициированных ими асинхронных запросов. Благодаря этому пользовательский интерфейс сохраняет свою интерактивность, и у пользователя не возникает чувства потери контакта с приложением.
Для синхронизации календарной информации с сервером Exchange Server приложению ActiveSync может требоваться телефонный звонок или иная форма операции соединения, на выполнение которой уходит длительное время. В процессе выполнения такой операции пользовательский интерфейс устройства отображает пояснительный текст, информирующий пользователя о текущем состоянии попытки создания соединения. Таким текстом может быть "Набор номера...", "Соединение с сервером", "Синхронизация расписания", "Загружено 12 из 20" и тому подобное. Такие несложные уведомляющие элементы пользовательского интерфейса не дают никакого выигрыша в производительности, но зато сохраняют в пользователе уверенность в том, что полезная для него работа продолжает выполняться, и удерживают его от попыток отмены запроса из-за того, что ответ на него затягивается.
Браузер Pocket Internet Explorer также может нуждаться в соединении с источниками данных, поиске IP-адресов URL и выполнении других задач в процессе загрузки Web-страниц. При выполнении подобных затяжных задач заголовок окна браузера обновляется постоянно отображая текст, информирующий пользователя о состоянии соединения. Кроме того, при загрузке Web-содержимого отображается анимация флага Windows. Оба указанных действия информируют пользователя о том, что выполнение операции продолжается.
В обоих случаях пользовательский интерфейс сохраняет свою интерактивность, а пользователям предоставляется возможность отменить операцию, если они того захотят. Наличие возможности в любой момент прекратить выполнение операции поддерживает в пользователе чувство уверенности в сохранении контроля над ситуацией, а непрерывное получение им информации о состоянии выполнения операции уменьшает вероятность того, что он ее отменит. Конечно, было бы лучше, если бы такие операции выполнялись мгновенно, но в тех случаях, когда это невозможно, следующее наилучшее решение заключается в поддержании интерактивности пользовательского интерфейса и непрерывном информировании пользователя о развитии ситуации.
Как ранее уже отмечалось, прежде чем приступить к проектированию мобильного приложения, важно выделить ключевые сценарии и возможности, которые будут определять сферу его применения. Далее, процесс разработки должен быть ориентирован на достижение высокой производительности приложения, ибо это будет гарантией того, что при составлении кода вы не загоните сами себя в угол. Если вы не в состоянии добиться того, чтобы выбранные вами ключевые сценарии и средства функционировали удовлетворительно, пересмотрите проект.
Следующей по очередности в списке важных задач стоит разработка подходящего пользовательского интерфейса. Подобно тому, как проблемы производительности решаются с учетом особенностей человеческого восприятия, отлично функционирующий пользовательский интерфейс должен проектироваться с учетом специфики целевых устройств. Пользовательский интерфейс, являющийся оптимальным для одного класса устройств, для устройств другого класса может оказаться далеко не лучшим. Учет специфики устройства всегда себя оправдывает
Будьте готовы к необходимости итеративного проектирования пользовательского интерфейса, особенно если вы только начинаете работать с мобильными устройств или приступаете к работе с новым классом устройств, форм-фактор которых отличен от тех, с которыми вы имели дело до этого. По мере накопления опыта работы с устройствами данного класса вы сможете проектировать пользовательские интерфейсы в более сжатые сроки, но вам все равно придется возвращаться к предыдущим этапам работы, чтобы все было сделано наилучшим образом.

Рис. 4.2. Проектирование пользовательского интерфейса, ориентированное на достижение высокой производительности
Будет неплохо, если вы возьмете за правило отделять логику выполнения приложения от логики представления информации, в результате чего внесение изменений в пользовательский интерфейс не будет влечь за собой необходимости изменения логики приложения. Это вдвойне справедливо, когда речь идет об устройствах. Абстрагируя логику выполнения приложения от логики представления информации, вы усиливаете свои позиции как в отношении пересмотра и улучшения пользовательского интерфейса для конкретного целевого устройства, так и в отношении быстрого переноса приложения на новые классы устройств. Сегодняшнее приложение для устройств PDA завтра вполне может стать приложением для смартфонов, если только код его пользовательского интерфейса тесно не переплетен с основной логикой приложения. Тщательное разделение логики приложения и логики интерфейса принесет вам неплохие дивиденды как в плане сопровождения кода, так и в плане его переносимости.
Удачный пользовательский интерфейс – довольные пользователи
(неудачный пользовательский интерфейс – ежедневный источник раздражения)
Ключевыми факторами проектирования пользовательского интерфейса, определяющими его успешность, являются обеспечение продуктивной работы конечного пользователя и сохранение постоянной способности интерфейса к интерактивному взаимодействию с пользователем. Очень важно, чтобы пользователи могли быстро выполнять основные сценарии работы приложения. Например, если конечным пользователям приходится часто вводить календарные даты, то используемые для этого
способы ввода данных должны обеспечивать как можно более высокую скорость, простоту и предсказуемость этого процесса. Если в других возможных ситуациях пользователям приходится часто выбирать элементы из длинного списка, то быстрее всего это можно сделать не тогда, когда все элементы отображаются в одном списке типа ListBox, а тогда, когда для этого разработан специальный графический пользовательский интерфейс, с помощью которого пользователи смогут быстро осуществить поиск нужного элемента. Простой перечень автомобильных запчастей только выиграет, если дополнить его схематическим изображением автомобиля, прикосновение к отдельным частям которого на дисплее будет перемещать вас в нужную часть списка. Аналогичным образом в медицинском приложении может быть использовано схематическое изображение человеческого тела. Корректный пользовательский интерфейс зависит от типа решаемых задач и специфики устройств, на которых выполняется приложение. Вот почему реализовать идею пользовательских интерфейсов, которые "пишутся однажды, выполняются везде", в случае мобильных устройств не всегда удается. Подходы такого типа просто не в состоянии обеспечить одинаково хорошие условия работы для пользователей устройств с различными размерами дисплеев и возможностями ввода.
С обеспечением высокой продуктивности работы пользователя тесно связано поддержание способности интерфейса к интерактивному взаимодействию. Пользовательский интерфейс приложений для мобильных устройств должен характеризоваться быстрым откликом. Это вовсе не означает, что пользователя вообще нельзя оставлять в состоянии ожидания; избежать этого иногда просто невозможно. Однако ни в коем случае нельзя заставлять пользователя лишь догадываться о том, выполняется ли запрошенная им операция или запрос необходимо повторить. Отсутствие каких-либо признаков активности устройства, получившего запрос на выполнение операции, вызывает у пользователей раздражение, поскольку психологически они настроены на то, что после нажатия кнопки, касания экрана или иного воздействия на органы управления находящегося у них в руках устройстве, должно обязательно что-то произойти.
Шаг 3: выберите подходящие модели данных и памятиВыбранные вами для мобильного приложения модели данных и памяти определяют, каким образом будет осуществляться управление объектами и ресурсами, хранящимися в памяти. И наоборот, модели памяти определяют, каким образом ваше приложение будет избавляться от ненужных данных и ресурсов, чтобы освободить память для своих нужд. Мобильные устройства заметно отличаются от своих настольных собратьев повышенными требованиями к эффективности управления данными и памятью.
В случае мобильных устройств крупными пулами памяти и файлами подкачки жертвуют ради уменьшения их размеров и снижения энергопотребления. Приложение, выполняющееся на мобильном устройстве, обитает уже не в роскошном особняке, а просто в хорошей городской квартире. Вместо спортивного автомобиля, используемого приложениями настольных компьютеров для быстрого объезда окрестностей, теперь имеется только мотоцикл. Считать, что ваше мобильное приложение "обитает" в условиях намного более ограниченного пространства – неплохая аналогия, которую полезно постоянно держать в голове в процессе проектирования моделей данных и памяти.

Рис. 4.3. Проектирование моделей данных и памяти, ориентированное на достижение высокой производительности
Те, кто программирует приложения для настольных компьютеров, исключая приложения, требующие огромных ресурсов памяти (например, сложные программы рисования часто обрабатывают числовые матрицы очень больших размерностей), в своем большинстве обычно даже не задумываются о том, какую модель памяти лучше использовать. Поэтому систематическое и заблаговременное освобождение памяти от хранящихся в ней ресурсов, необходимость в которых отпала, как правило, не производится. Любые необходимые данные сразу же загружаются в память без предварительной ее очистки от ненужных данных. Непрерывная загрузка данных и ресурсов в память продолжается либо из-за беспечности программиста, либо исходя из того, что они еще могут понадобиться пользователю. Если уж приложение позаботилось о загрузке некоторых данных или изображений из сетевого ресурса, то почему бы не подержать их в памяти подольше, чтобы сразу же предоставить их пользователю, если ему захочется вновь обратиться к этому же ресурсу? В случае приложений, предназначенных для настольных компьютеров, такой подход является в значительной степени оправданным; находящиеся в памяти неиспользуемые данные, в конечном счете, сбрасываются на жесткий диск, а вызов необходимой нужной страницы данных в память выполняется гораздо быстрее, чем повторное подключение к сети и посылка запроса. В распоряжении у приложения имеется целый дом, и неиспользуемые вещи можно просто-напросто разместить где-то на чердаке.
Как нами ранее уже обсуждалось, в случае мобильных устройств наблюдается совершенно иная ситуация. Учитывая ограниченность объема доступной памяти и отсутствие вспомогательных накопителей, которые можно было бы использовать для временного хранения страниц памяти, разработчики обязаны использовать память и ресурсы весьма расчетливо. Разработчик мобильного приложения обязан целенаправленно выбрать модель данных, в соответствии с которой будет осуществляться управление хранением данных в памяти и сборкой мусора. В случае устройств правильная стратегия часто заключается в освобождении памяти от данных или явном их перемещении на карту флэш памяти и повторном вызове данных в память, когда они вновь потребуются.
Удачная модель данных означает высокую производительность и гибкость дизайна
(неудачная модель – заведомо низкую производительность)
Если согласиться с тем, что существует искусство создания замечательных приложений, то важнейшей его составляющей является умение выбирать наиболее подходящие модели данных. Рациональное управление памятью и ресурсами подразумевает следующее: 1) приобретение навыков экономного расходования памяти и хранения в ней только тех объектов, в которых действительно существует острая необходимость и которые будут немедленно использоваться, 2) использование кэширования важных данных на устройстве, но вне активной памяти и 3) перемещение остальных данных на накопитель, находящийся за пределами устройства. Научившись этим трем вещам, вы пройдете значительную часть дистанции, отделяющей вас от создания великолепно функционирующих приложений, работа с которыми будет доставлять удовольствие пользователям.







