Текст книги "Программирование мобильных устройств на платформе .NET Compact Framework"
Автор книги: Иво Салмре
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 21 (всего у книги 69 страниц)
// + Отображение дополнительного текста, сообщающего пользователю // о том, что происходит
// + Визуальная индикация окончания выполнения работы // (исчезновение курсора ожидания)
// – Пользовательский интерфейс не способен к отклику в процессе работы
// + По завершении выполнения задачи конечный пользователь узнает об этом,
// а пользовательский интерфейс восстанавливает способность к отклику
// + Текстовые сообщения информируют пользователя о том, что происходит
//–
private void button3_Click(object sender, System.EventArgs e) {
//Предоставить пользователю текст, информирующий его обо всем происходящем
label1.Text = "Ждите! Работа выполняется!";
//Заставить интерфейс обновить текст
//(иначе он сделает это только тогда, когда будет перерисовывать сообщение,
//a это может произойти и после выхода из данной функции)
label1.Update();
//Отобразить курсор ожидания
System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.WaitCursor;
//Имитировать выполнение работы путем создания паузы
//продолжительностью 2,8 секунды
System.Threading.Thread.Sleep(2800);
//Необязательное дополнительное обновление состояния
label1.Text = "Ждите! Работа близка к завершению!";
label1.Update();
//Имитировать выполнение работы путем создания паузы
//продолжительностью 1,2 секунды
System.Threading.Thread.Sleep(1200);
//Известить пользователя текстовым сообщением о завершении работы
//(текст обновляется всякий раз, когда ПИ выполняет обычное
//обновление экрана)
label1.Text = "Работа успешно завершена!";
//Избавиться от курсора ожидания
System.Windows.Forms.Cursor.Current = System.Windows.Forms.Cursors.Default;
}
Разумеется, лучше всего, если пользовательский интерес никогда не теряет способности к отклику, однако для сравнительно коротких периодов задержки или случаев, когда работа должна выполняться в синхронном режиме, существует много косметических приемов, которые обеспечивают создание для пользователей комфортных условий работы с приложением и сводят влияние раздражающих факторов к минимуму.
Максимальная продолжительность отображения курсора ожиданияСуществуют определенные ограничения на длительность интервалов времени, на протяжении которых еще допускается заставлять пользователя дожидаться восстановления способности пользовательского интерфейса к отклику. Отображение курсора ожидания и периодическое обновление информации, адресованной пользователю, позволяет несколько удлинить эти промежутки, но все-таки существует некоторый предел, при превышении которого пользователи начинают испытывать неудобства и раздражаться из-за того, что приложение не отвечает на их запросы. Допустимая предельная длительность периода ожидания для большинства случаев составляет около пяти секунд, однако должна быть снижена до одной-двух секунд при доступе к часто используемым функциональным средствам.
В документации к своему проекту вы должны указать не только то, что собираетесь повысить комфортность условий работы пользователей с приложением при помощи курсоров ожидания, но и максимально допустимую длительность задержек. Задержки свыше указанных пороговых значений должны влечь за собой доработку проекта. Явное формулирование этой задачи служит гарантией того, что конечные пользователи будут чувствовать себя одинаково комфортно на протяжении всего сеанса работы с вашим мобильным приложением.
Максимальная продолжительность загрузки/сохранения данных, а также запуска/закрытия приложенияЗадачи, для выполнения которых требуется длительное время, часто можно вытолкнуть в фоновый поток, так что пользователь даже не будет замечать, что задача выполняется долго. Если задача выполняется фоновым потоком, то говорят, что она выполняется в "асинхронном режиме". Однако в некоторых ситуациях для того, чтoбы приложение могло продолжить работу, требуется предварительно выполнить некоторые операции. К числу типичных ситуаций такого рода относятся следующие:
■ Загрузка документов. Если приложение предназначено для работы с документами, то для того, чтобы оно могло продолжить работу, может потребоваться загрузка документа.
■ Выход из приложения и сохранение загруженных данных. Если пользователь хочет быть уверенным в том, что данные успешно сохранены, то это должно осуществляться синхронно с завершением работы приложения.
■ Запуск приложения и инициализация данных. Как и в случае загрузки документов, может существовать необходимая для запуска приложения информация, которая должна быть загружена и обработана, прежде чем приложение сможет продолжить дальнейшее выполнение
Даже задержки, обусловленные необходимостью выполнения обязательных задач, могут вызывать у пользователя раздражение. В некоторых случаях заставки, курсоры ожидания или индикаторы выполнения позволяют несколько сгладить ситуацию, но не более того. 30-секундный запуск всегда остается 30-секундным запуском, и пользователей мобильных устройств, которым надо поработать всего 20 секунд, а затем вернуть устройство обратно в карман, это раздражает. Как выше уже отмечалось, вы должны явно устанавливать максимальные интервалы задержек, которые еще можно считать допустимыми с точки зрения пользователя в подобных ситуациях. Если ваше приложение превышает эти допуски, в проект необходимо ввести соответствующие изменения. Действительно ли вы загружаете лишь минимальный объем данных, требуемых при запуске приложения? Могут ли данные запуска кэшироваться или сохраняться в формате, в котором они будут быстрее загружаться? Если при запуске требуются сетевые данные, то могут ли они сохраняться в локальной кэш-памяти? Могут найтись самые разнообразные творческие решения, которые должны быть исследованы с точки зрения повышения производительности приложения в ключевых точках, и иногда, чтобы обеспечить достижение необходимых показателей, на которые рассчитывают пользователи мобильных устройств с немедленным доступом, вы должны будете пересмотреть некоторые из положений проекта.
Накладные расходы по обработке исключений
Современные среды времени выполнения предоставляют структурную обработку исключений, позволяющую коду надежно справляться с разрешением исключительных ситуаций. Программисты могут перехватывать исключения, возбуждаемые низкоуровневым кодом среды выполнения, но могут возбуждать и перехватывать также собственные исключения. Структурная обработка исключений является чрезвычайно надежным средством, позволяющим средам выполнения управляемого кода, компонентам и приложениям восстанавливать работоспособность после непредвиденных сбоев и возобновлять нормальный режим функционирования. При возбуждении исключения среда выполнения принимает управление на себя и просматривает стек в поиске соответствующего обработчика, который будет использоваться для перехвата исключения. При этом гораздо важнее то, чтобы стек был развернут корректно, а не то, чтобы обработка исключения была осуществлена столь же быстро, как и выполнение обычного кода. Как следствие, издержки на возбуждение и обработку исключений могут приводить к некоторому ухудшению производительности.
Чтобы сохранить производительность на высоком уровне, следует избегать создания предпосылок для частого возбуждения исключений при нормальном выполнении приложения. В коде алгоритмов вашего мобильного приложения должны быть предусмотрены специальные меры, позволяющие избегать возбуждения и перехвата исключений в условиях нормального выполнения. Проектируйте собственные алгоритмы таким образом, чтобы исключения возбуждались лишь в действительно исключительных ситуациях. В обычных условиях ваше приложение должно выполняться так, чтобы не было сгенерировано ни одного исключения.
Если обнаруживается, что в условиях нормального выполнения приложения ему приходится обрабатывать исключения, возбуждаемые средой времени выполнения, или собственные исключения, проанализируйте код и измените его таким образом, чтобы избежать создания подобных исключительных ситуаций.
Структурная обработка исключений является мощным средством объектно-ориентированного программирования, но ее применение требует осмотрительности. Умелое использование структурной обработки исключений при размещении объектов в памяти и освобождении памяти от объектов, необходимость в которых отпала, способно упростить процесс программирования и сделать его более надежным, однако при применении этого средства неопытными программистами производительность приложения может резко ухудшиться.
Пример сравнения эквивалентных алгоритмов, в которых возбуждение исключений соответственно используется или не используетсяВ листинге 7.4 представлен пример приложения, в котором сравниваются два алгоритма, решающие одну и ту же задачу, но значительно различающиеся между собой показателями производительности. Один алгоритм осуществляет сложение двух чисел и возвращает их сумму, а также булевское значение, которое указывает на знак результата – положительный или отрицательный. Другой алгоритм также складывает два числа и возвращает результат, но если результат является отрицательным числом, возбуждается исключение. Как и следовало ожидать, алгоритм, в котором в процессе нормального выполнения приложения используется возбуждение исключений, по своей производительности разительно отличается в худшую сторону от алгоритма, в котором исключения не используются. Хотя в абсолютном смысле оба алгоритма paботают довольно быстро и выполняют 10000 итераций менее чем за 2,5 секунды, один из них работает более чем в 350 раз быстрее по сравнению с другим; в случае вычислений, в которых интенсивно используются циклы, это различие может иметь критическое значение.
Результаты выполнения тестовых вычислений на физическом устройстве Pocket PC без подключения отладчика представлены в табл. 7.1. Как следует из приведенных в этой таблице данных, алгоритм, в котором возбуждение и перехват исключений не используются, работает в несколько сотен раз быстрее. В нашем примере применяются очень простые алгоритмы и простой механизм возбуждения и перехвата исключений; процесс обработки исключений требует осуществления средой выполнения сравнительно небольшого объема операций по освобождению памяти и разворачиванию стека. Чрезвычайная простота выполняемых вычислений позволяет более отчетливо выявить влияние обработки исключений на производительность приложения. В зависимости от сложности алгоритма и объема работы, необходимой для обработки возбужденных исключений, мы будем получать различные результаты, но можно сделать один совершенно очевидный вывод накладные расходы на обработку часто возбуждаемых исключений могут оказаться значительными, так что использования исключений в условиях нормального выполнения кода следует избегать, если только на то нет особой необходимости.
Таблица 7.1. Сравнительные показатели производительности простого алгоритма, выполняемого с возбуждением исключений и без такового
| 10000 | 0,006 | 2,202 | 367 |
| 10000 | 0,006 | 2,201 | 367 |
| 100000 | 0,061 | 22,716 | 372 |
| 100000 | 0,055 | 22,834 | 415 |
| 100000 | 0,055 | 22,995 | 418 |
Выполнение используемого в качестве примера приложения на эмуляторе Pocket PC иллюстрирует рис. 7.2.

Рис. 7.2. Пример выполнения приложения, позволяющего оценить влияние обработки исключений на показатели производительности
НА ЗАМЕТКУ
Следует отметить, что поскольку обработка исключений входит в состав базовых средств времени выполнения и отладки .NET Compact Framework и Visual Studio .NET, то производительность приложения может в заметной степени меняться в зависимости от двух обстоятельств
1. Производительность кода, генерирующего исключения, может значительно ухудшаться, если программа выполняется с подключенным отладчиком. Это объясняется тем, что в этом случае среда уведомляется о возбуждении исключений, даже если они перехватываются кодом приложения. Это означает, что алгоритмы, выполнение которых сопровождается возбуждением многочисленных исключений, при подключенном отладчике будут работать очень медленно! Вы не должны забывать об этом на стадиях проектирования и разработки приложения; возбуждение множества исключений может замедлить разработку. Обычно поведение среды разработки не должно вынуждать вас к изменению способа написания кода, но в данном случае вы должны включить в документ проекта дополнительный пункт, в соответствии с которым следует избегать многократного возбуждения исключений в часто выполняемых циклах.
2. Различия в производительности кода, использующего обработку исключений, при его выполнении на эмуляторах и физических устройствах весьма заметны. Чтобы получить наиболее точные данные о том, каким образом возбуждение и обработка исключений влияют на производительность вашего приложения, соответствующие тесты должны выполняться на физических устройствах при отключенном отладчике.
Представленный в листинге 7.4 код следует включить в форму проекта для Pocket PC. Для сборки и запуска приложения необходимо выполнить следующие действия:
1. Запустите Visual Studio .NET (2003 или более позднюю версию) и создайте приложение C# Smart Device Application.
2. Выберите в качестве целевой платформы Pocket PC. (Для вас будет автоматически сгенерирован проект, и на экране появится конструктор форм Pocket PC.)
3. Добавьте в форму перечисленные ниже элементы управления. Возможная схема расположения элементов управления на форме показана на рис. 7.2.
• TextBox; переименуйте его в textBoxNumberAttempts.
• Button; переименуйте его в buttonRunNoExceptionCode.
• Button; переименуйте его в buttonRunExceptionCode.
• ListBox; оставьте в качестве его имени listBox1.
4. Выполните по отношению к каждой из кнопок следующие действия. Дважды щелкните на кнопке в конструкторе формы. В автоматически сгенерированной и подключенной функции обработчика событий введите один из приведенных ниже кодов с соответствующим именем button<ИмяКнопки>_Click.
5. Введите оставшуюся часть приведенного ниже кода.
6. Установите для свойства MinimizeBox значение FALSE. Это приведет к тому, что во время выполнения приложения в правом верхнем углу формы появится кнопка OK, используя которую легко завершить выполнение приложения; эта возможность оказывается особенно удобной при многократном тестировании формы.
7. Добавьте в проект новый класс, назовите его PerformanceSampling и, предварительно удалив его текущее содержимое, введите в него код, показанный в листинге 7.1.
8. Запустите приложение на физическом устройстве или эмуляторе, нажав для этого клавишу
Листинг 7.4. Сравнение производительности двух алгоритмов, в одном из которых используются исключения, а во втором – нет
//Примечание. В этом примере используется класс PerformanceSampling,
// определенный ранее в этой главе. Убедитесь в том, что
// этот класс включен в проект
//ТЕСТОВАЯ ФУНКЦИЯ:
//Сложить 'n1' и 'n2' и возвратить результат
//в 'n3'
// Возвращаемое значение:
// TRUE: если результат положителен
// FALSE: если результат отрицателен
bool returnFalseIfLessThanZero_Add2Numbers(int n1, int n2, out int n3) {
n3 = n1 + n2;
//Результат меньше 0?
if (n3 < 0) {
return false;
}
return true;
}
//===========================================================
// ТЕСТОВАЯ ФУНКЦИЯ:
//
//Сложить 'n1' и 'n2' и возвратить результат
//в 'n3'
//
//Если 'n3' меньше 0, то функция ПЕРЕДАЕТ УПРАВЛЕНИЕ ОБРАБОТЧИКУ ИСКЛЮЧЕНИЙ.
//B противном случае возвращается TRUE
//===========================================================
bool exceptionIfLessThanZero_Add2Numbers(int n1, int n2, out int n3) {
n3 = n1 + n2;
//Результат меньше 0?
if (n3 < 0) {
throw new Ехсерtion("Результат меньше 0!");
}
return true;
}
//===========================================================
//Осуществляет многократные вызовы простой функции и
//измеряет общее время выполнения
//
//Вызываемая функция НЕ приводит к возбуждению исключений
//===========================================================
private void buttonRunNoExceptionCode_Click(object sender, System.EventArgs e) {
const int TEST_NUMBER = 0;
int numberIterations;
numberIterations = System.Convert.ToInt32(textBoxNumberAttempts.Text);
//Отобразить количество итераций, которые предстоит выполнить
listBox1.Items.Add("=>" + numberIterations.ToString() + " итераций");
int count_SumLessThanZero;
int dataOut;
//–
//Запустить таймер
//–
PerformanceSampling.StartSample(TEST_NUMBER, "Исключения отсутствуют");
//–
//Выполнить цикл, в котором осуществляется вызов функции
//–
count_SumLessThanZero = 0;
bool sumGreaterThanZero;
for(int i = 0; i < numberIterations; i++) {
//=========================
//Вызвать тестовую функцию!
//=========================
sumGreaterThanZero = returnFalseIfLessThanZero_Add2Numbers(-2, -3, outdataOut);
if (sumGreaterThanZero == false) {
count_SumLessThanZero++;
}
} //конец цикла
//–
//Остановить таймер
//–
PerformanceSampling.StopSample(TEST_NUMBER);
//–
//Показать результаты пользователю
//–
if (count_SumLessThanZero == numberIterations) {
System.Windows.Forms.MessageBox.Show("Тест выполнен");
listBox1.Items.Add(PerformanceSampling.GetSampleDurationText(TEST_NUMBER));
} else {
System.Windows.Forms.MessageBox.Show("При выполнении теста возникали осложнения");
}
}
//конец функции
//===========================================================
//Осуществляет многократные вызовы простой функции и
//измеряет общее время выполнения.
//
//Вызываемая функция ВОЗБУЖДАЕТ исключения
//===========================================================
private void buttonRunExceptionCode_Click(object sender, System.EventArgs e) {
const int TEST_NUMBER = 1;
//Получить количество итераций
int numberIterations;
numberIterations = System.Convert.ToInt32(textBoxNumberAttempts.Text);
//Отобразить количество итераций, которые надлежит выполнить
listBox1.Items.Add("=>" + numberIterations.ToString() + " итераций");
int count_SumLessThanZero;
int dataOut;
//–
//Запустить таймер
//–
PerformanceSampling.StartSample(TEST_NUMBER , "Перехват исключения");
//–
//Выполнить цикл, в котором осуществляется вызов функции
//–
count_SumLessThanZero = 0;
bool sumGreaterThanZero;
for (int i = 0; i < numberIterations; i++) {
try {
//=========================
//Вызвать тестовую функцию!
//=========================
sumGreaterThanZero = exceptionIfLessThanZero_Add2Numbers(-2, -3, outdataOut);
} catch {
count_SumLessThanZero++;
}
} //конец цикла
//–
//Остановить таймер
//–
PerformanceSampling.StopSample(TEST_ NUMBER);
//–
//Показать результаты пользователю
//–
if (count_SumLessThanZero == numberIterations) {
System.Windows.Forms.MessageBox.Show("Тест выполнен");
listBox1.Items.Add(PerformanceSampling.GetSampleDurationText(ТЕST_NUMBER));
} else {
System.Windows.Forms.MessageBox.Show("При выполнении теста возникали осложнения");
}
}
Резюме
Высокая производительность в сочетании с хорошо продуманным пользовательским интерфейсом – вот что придает элегантность мобильному приложению. Имея в своем активе высокие показатели производительности, вы можете расходовать капитал разработки на расширение функциональных возможностей приложения. Заручившись этим, вы можете смело подыскивать для приложения новые интересные области применения. Приложение с низкой производительностью – это все равно, что дырявая крыша над головой; вы потратите все свое время на то, чтобы перебегать от одной прорехи к другой, латая дыры, пока все равно не придется менять крышу в самый неожиданный момент, а такой ситуации не позавидуешь. Хорошая производительность достигается сознательными усилиями и является результатом планируемых действий, плохая производительность – следствие допущенных ошибок. Стремитесь к обеспечению высокой производительности и направляйте на это основные усилия, когда разрабатываете мобильное приложение.
По мере написания кода и разработки приложения между его модулями будут формироваться различного рода взаимозависимости. Чем больше кода вы напишете, тем более жестким будет становиться приложение, оставляя вам все меньше возможностей для радикального изменения системы. Обеспечение высокой производительности часто требует радикального пересмотра проекта, и поэтому за решение проблем этого рода лучше всего браться сразу же после того, как они становятся очевидными. Для этого существуют два способа: 1) свяжите с каждым из ключевых этапов разработки определенные цели, связанные с производительностью, и не считайте этап успешно завершенным, пока эти цели не будут достигнуты, и 2) периодически измеряйте и анализируйте показатели производительности.
Производительность, как к ней ни подходить, воспринимается субъективно. Если пользователю кажется, что производительность приложения низкая, оно обладает слабой интерактивностью или им неудобно пользоваться, то из такой его оценки и надо исходить. Уделяйте пристальное внимание тем деталям, от которых зависит, какой будет субъективная оценка, данная вашему приложению пользователями, – хорошей или плохой. По этой причине весьма полезно создавать прототипы идей и привлекать для их тестирования пользователей реальных устройств. Те, кто пользуется мобильными устройствами, желают получать от них осязаемые подтверждения того факта, что интерактивная связь между пользователем и устройством ни на минуту не разрывается. Не чувствуя ответной реакции устройства после отправки ему запроса, пользователи очень скоро начинают беспокоиться. Всегда старайтесь, чтобы в процессе работы вашего приложения беспокойство пользователя по этому поводу было сведено к минимуму. Не заставляйте пользователей гадать – происходит что-то или не происходит; незамедлительно давайте им знать о том, что устройство занято обслуживанием их запроса.
Наконец, подходите ко всему творчески. Напряженная работа и творческий подход позволяют решить практически любую проблему. Если вы приходите к выводу, что дальнейшее повышение производительности связано с фундаментальными ограничениями ваших алгоритмов, то не исключено, что настал момент повторно рассмотреть основные цели приложения и постараться изыскать новые способы их достижения. Великие идеи рождаются в процессе решения проблем, которые сразу кажутся неприступными. Творческое рассмотрение проблем производительности будет для вас не только увлекательным занятием, но и сторицей себя окупит.







