Книга: Программирование мобильных устройств на платформе .NET Compact Framework

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

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

Важным аспектом производительности приложения является его интерактивность. Наилучшим вариантом было бы полное отсутствие каких-либо задержек во взаимодействии пользователя с пользовательским интерфейсом, однако в некоторых случаях это оказывается просто невозможным. Если необходимо выполнить работу, результатов которой пользователь должен дожидаться в течение длительного времени, то следует приложить некоторые усилия к тому, чтобы сделать условия такого ожидания настолько комфортными, насколько это возможно. Считайте, что эта задача столь же важна, как и проектирование помещения, в котором пациенты вынуждены дожидаться своей очереди при посещении врача. Было бы замечательно, если бы к врачу можно было зайти вообще безо всякой очереди, но это не представляется возможным (Если вам известны исключения, дайте мне об этом знать!) Условия ожидания были бы значительно худшими, если бы не было человека в регистратуре, который делает отметку о вашем визите, если бы вам не сообщали, сколько примерно времени вам придется ждать, если бы окружающая обстановка не было комфортной или если бы вам не предоставляли возможность полистать или почитать какой-нибудь журнал, дабы скоротать время ожидания. Прекрасным и эстетически привлекательным элементом интерьера, помогающим занять ваше внимание, служит, в частности, аквариум. Многое дает и регистрация вашего прихода. Условия визита еще более улучшатся, если регистратор сообщит вам, как долго продлится ваше ожидание, а также предложит газету и прохладительный напиток, чтобы вы чувствовали себя более комфортно. (Опять-таки, если вам известна клиника, где именно так все и происходит, обязательно сообщите мне об этом.)

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

? Наихудшие условия: полное отсутствие связи с пользователем. Картина приложения, которое не реагирует на запросы и не дает пользователю ни малейшего намека на то, что работа для него выполняется, очень напоминает зависание или блокирование приложения. Раздражение пользователя быстро нарастает и он начинает лихорадочно щелкать кнопками или давить на экран, чтобы добиться от приложения хоть какого-нибудь ответа. В результате этого остается ряд случайных непреднамеренных событий, которые приложению придется обрабатывать, когда управление будет вновь передано пользовательскому интерфейсу. Если предыдущие попытки успеха не принесли, то спустя короткое время, пользователь, вероятнее всего, попытается закрыть приложение, переключиться на другое приложение или извлечь из устройства батарею для перезарядки. (В случае мобильных телефонов это не составляет никакого труда.) Ситуация еще более усугубляется, когда мобильное приложение не предоставляет пользователю никакой визуальной индикации того, что работа завершена. Если предположить, что в течение всего времени, пока приложение было занято вычислениями, пользователь терпеливо ждал, не нажимая никаких кнопок и не вынимая батарею, то как он узнает о том, что к приложению вернулась способность реагировать на запросы? Старайтесь всегда избегать ситуаций, в которых приложение выглядит вполне нормально и, казалось бы, должно отвечать на запросы, но на самом деле этого не происходит. 

? Улучшенные условия: отображение курсора ожидания в течение того времени, пока приложение не в состоянии отвечать на запросы. Отображение курсора ожидания преследует две важные цели. Во-первых, он извещает конечных пользователей о том, что работа для них выполняется, а во-вторых, его исчезновение указывает на то, что работа завершена и приложением вновь можно пользоваться. Если учесть, что отображение курсора ожидания обычно не составляет никакого труда, то нежеланию приложить даже столь незначительные усилия к тому, чтобы улучшить интерактивные свойства приложения, не может быть никаких оправданий. Любая операция, для выполнения которой требуется более нескольких десятых долей секунды, тормозит пользовательский интерфейс до такой степени, что пользователь сможет это почувствовать, и поэтому нет ни малейших оснований для того, чтобы в этой ситуации не дать пользователю понять, что приложение выполняет нужную для него работу. Используя курсор ожидания, вы на несколько секунд "покупаете" понимание и терпение со стороны пользователя устройства, но если выполнение работы занимает более нескольких секунд, то вам следует прибегнуть к более информативному индикатору выполнения, нежели простой курсор ожидания. Кроме того, если почти любое действие пользователя сопровождается появлением курсора ожидания, вам следует позаботиться об оптимизации мобильного приложения и выполнении части обработки в фоновом режиме; задержки могут быть приемлемыми в некоторых ситуациях, но далеко не во всех. 

? Лучшие условия; отображение индикатора выполнения или сопроводительного текста, поясняющего, что именно происходит в данный момент. Если операция, завершения которой вынужден дожидаться пользователь, длится более нескольких секунд, следует подумать об использовании индикатора выполнения или текстовых пояснений, которые информируют пользователя о том, что происходит в данный момент, отображают процентную долю оставшейся работы или оценку времени, оставшегося до завершения операции. Отображение курсора ожидания не препятствует отображению текста, информирующего пользователя о том, как продвигается работа. Пояснительный текст, который говорит пользователю о том, какая именно работа для него выполняется, можно отображать позади курсора ожидания. Полезной будет любая информация, указывающая на степень выполнения работы. Информативный текст полезен, в частности, тем, что помогает поддерживать в пользователе чувство причастности к вычислительному процессу. Так, алгоритм, загружающий информацию с сервера, может отображать такую последовательность обновляющихся сообщений: "Поиск сервера", "Сервер найден. Выполняется регистрация", "Загружается информация (10%)", "Загружается информация (60%)", "Готово!" Информация такого рода порождает в пользователе ощущение участия в процессе загрузки. Обратите внимание на важность последнего сообщения, говорящего о том, что выполнение операции завершилось успешно. Кроме того, если в процессе загрузки возникают какие-либо трудности, то текстовая информация, которая содержится в указанных обновляемых сообщениях, может помочь пользователю в преодолении возникших проблем.

При написании кода, предоставляющего пользователю периодически обновляемую информацию о степени завершения задач, которые выполняются в течение длительного времени, может потребоваться явная перерисовка элемента управления. Если не предусмотрено явное обновление форм и элементов управления, то они, как правило, лишь помещают сообщения, требующие перерисовки экрана, в очередь, непринудительное обновление сообщений на экране происходит только тогда, когда у системы находится время для их обработки. Поскольку ваш вычислительный процесс может выполняться в том же потоке, что и код пользовательского интерфейса, выполнение полезной работы будет препятствовать обработке сообщений, помещенных в очередь, до тех пор, пока управление не будет возвращено пользовательскому интерфейсу. Для разрешения этой проблемы в .NET Compact Framework для каждого элемента управления предусмотрен метод Update(). Вызов этого метода приводит к немедленной перерисовке элемента управления. В листинге 11.5 представлен простой рабочий алгоритм, который периодически обновляет в элементе управления Label текст, информирующий о степени выполнения приложения. Если только не осуществить вызов label1.Update() при обновлении текста в Label, то новый текст будет отображен лишь после завершения работы. 

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

Если длительная задача выполняется в высокоприоритетном потоке (том же, в котором выполняется код пользовательского интерфейса), то предоставление пользователю возможности отменить ее выполнение, хотя и осложняется, по-прежнему остается осуществимым. Как и в случае обновления пользовательского интерфейса в процессе выполнения интенсивных вычислений, реакция на пользовательские запросы, в том числе и на щелчок на кнопке отмены выполнения, возможна лишь при условии обработки сообщений потока пользовательского интерфейса. В .NET Compact Framework предусмотрено решение для подобных ситуаций, но аналогичные концепции используются, вероятно, и в каркасах приложений для других устройств. Вызов статического метода System.Windows.Forms.Application.DoEvents() приводит к принудительной обработке всех сообщений, находящихся в очереди данного потока, до того, как выполнение сможет быть продолжено. Это означает, что будут обработаны и сообщения, требующие перерисовки пользовательского интерфейса, в том числе ожидающие обработки щелчки на кнопках и нажатия клавиш. Если вызывать метод DoEvents() достаточно часто (несколько раз в секунду), то можно обеспечить сохранение интерактивности пользовательского интерфейса без малейшего ущерба для выполнения текущей задачи.

Может показаться, что метод DoEvents() является панацеей от всех бед, но это не так. Вызывая метод DoEvents(), необходимо проявлять осторожность, поскольку при этом могут порождаться в высшей степени нежелательные тонкие эффекты. Так как вызов метода DoEvents() приводит к обработке всех сообщений, прежде чем управление будет возвращено вызвавшему его коду, то это может сопровождаться вызовом обработчиков событий таймера и повторными вхождениями в выполняющиеся в данный момент обработчики событий. В результате этого может сложиться такая ситуация, при которой мы будем иметь множество вложенных вызовов обработчиков событий, связанных с элементами пользовательского интерфейса, и таймеров, если метод DoEvents() вызывается внутри обработчика события и новое сообщение о событии помещается в очередь еще до завершения обработки первого события. Собираясь использовать метод DoEvents(), вы должны использовать при написании кода обработчиков событий своего приложения технологию "безопасного программирования" (defensive programming), которая предполагает тщательную проверку выполнения всех допустимых условий. Настоятельно рекомендуется помещать все подобные проверки в самом начале кода обработчиков событий, чтобы осуществить немедленный выход из функции в случае, если вхождение в нее является повторным. Прежде чем осуществить вхождение в блок кода, в котором выполняется задача, требующая длительного времени, и в процессе этого вызывается метод DoEvents(), убедитесь в том, что для всех элементов управления, генерация которыми событий должна быть запрещена, значение свойства Enabled установлено в false; благодаря этой мере пользователи будут лишены возможности выполнять щелчки на указанных элементах управления, тем самым загромождая очередь событий. Ситуации, в которых возможность повторного вхождения в обработчики событий является желательной, встречаются крайне редко, ибо в этом случае код становится чрезвычайно запутанным и трудно поддается отладке. Если вы можете выбирать между использованием метода DoEvents() и созданием фонового потока для выполнения основных вычислений, то я бы рекомендовал почти всегда отдавать предпочтение хорошо продуманному последнему решению. Каким бы сложным с концептуальной точки зрения ни казалось использование нескольких потоков, модель их выполнения обычно оказывается более простой и предсказуемой, чем смесь кодов каркаса и приложения, вызываемая при использовании метода DoEvents(). Ничто из вышесказанного не является чем-то специфическим для мобильных устройств; те же самые проблемы возникают и в случае настольных компьютеров, однако мы акцентируем на них внимание, исходя из повышенных потребностей конечных пользователей в отношении интерактивного взаимодействия с приложением в случае мобильных устройств. Тем не менее, иногда применение метода DoEvents() может оказаться полезным, и я описываю здесь этот подход, одновременно предостерегая вас: "Caveat emptor!" ("Пусть покупатель будет бдителен!"), то есть действуйте на свой страх и риск. Если вы покупаете билет на DoEvents(), то должны быть готовы ко всем подъемам и спускам на этом маршруте. 

? Наилучшие условия: найдите такой способ выполнения задачи, которые никоим образом не приводят к блокированию действий пользователя. Если ваше мобильное приложение способно вытолкнуть выполнение работы в фоновый поток, предоставляя пользователю возможность продолжить работу с другими задачами и своевременно уведомляя его о завершении первой задачи, то можете считать, что Чаша Грааля интерактивных возможностей приложения — в ваших руках. Действуя подобным образом, вы должны тщательно продумать модель выталкивания выполнения работы в фоновый поток. Вам необходимо решить, каким образом следует информировать пользователя о ходе выполнения работы, сохранив для него возможность управления выполнением фоновой задачи, но при этом не создавать никаких ощутимых неудобств в отношении взаимодействия пользователя с высокоприоритетным потоком пользовательского интерфейса. В качестве примера удачного применения этой стратегии в случае приложений для настольных компьютеров можно привести поддержку в Microsoft Word печати документов в фоновом режиме с одновременным предоставлением пользователю возможности продолжения работы по редактированию активного документа в высокоприоритетном потоке. Это средство очень полезно и кажется простым, но, чтобы все это работало, проектировщикам пришлось хорошенько потрудиться

Оглавление книги


Генерация: 1.632. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз