Программирование мобильных устройств на платформе .NET Compact Framework
Шрифт:
Хорошая организация управления памятью предполагает достижение баланса множества факторов и эффективное использование памяти, с которой работает приложение. Не имеет смысла стремиться к снижению объема информации о глобальном состоянии до минимума, если приложению для нормального выполнения потребуется постоянно создавать и уничтожать те же самые объекты в виде временных объектов. Точно так же, не стоит хранить в памяти состояние приложения в таком объеме, который приводит к образованию дефицита рабочего пространства, в результате чего эффективное выполнение алгоритмов приложения становится невозможным без непрерывного освобождения памяти посредством механизма сборки мусора. Все, что для этого требуется — это обеспечение разумного компромисса между двумя вышеупомянутыми противоречащими друг другу требованиями.
Производительность и многопоточное выполнение
В
В то же время для использования фоновых потоков в приложениях существуют веские причины. Наиболее привлекательной областью применения потоков является сохранение способности вашего мобильного приложения к интерактивному взаимодействию с пользователем в тех случаях, когда выполнение задачи занимает длительное время или же о длительности ее выполнения заранее ничего сказать невозможно. Длительное выполнение характерно для тех задач, которые связаны с интенсивными вычислениями или интенсивной алгоритмической обработкой. Задачи, время выполнения которых является неопределенным, требуют доступа к ресурсам, которыми вы непосредственно управлять не можете. (Так, спрогнозировать длительность задержек в случае доступа к информации через сеть практически невозможно.) Для такого рода операций отличные возможности предоставляет работа в асинхронном режиме.
Суть простейшей и наиболее эффективной модели многопоточного выполнения состоит в том, чтобы иметь один основной поток, взаимодействующий с пользователем, и ряд фоновых потоков, предназначенных для выполнения асинхронных задач. Основной высокоприоритетный поток может производить периодический опрос с целью выяснения того, не завершилось ли выполнение фоновых задач или не поступила ли промежуточная информация о состоянии выполнения той или иной задачи, которую следует довести до ведома пользователя. Такие потоки фоновой обработки могут либо создаваться по требованию, либо существовать в виде пула потоков, ожидающих появления работы, которую необходимо выполнить. Создание потоков по требованию концептуально проще создания диспетчера пула потоков. Если готовой инфраструктуры, которая позволяла бы управлять фоновым выполнением, не существует, то в случае мобильных приложений я порекомендовал бы создавать для этой цели потоки по требованию. В состав каркасов приложений могут входить встроенные средства, позволяющие выполнять задачи в асинхронном режиме, и эту возможность необходимо всегда проверять, прежде чем браться за создание собственной модели многопоточного выполнения; какой смысл заново изобретать колесо, если оно давным-давно существует.
Производительность и уровни абстракции API-интерфейсов
Очень важно выбрать для работы подходящий уровень абстракции. Высокоуровневые абстракции API-интерфейсов предоставляют упрощенную, удобную в работе и хорошо проверенную программную модель, но это часто влечет за собой дополнительные накладные расходы. Низкоуровневые API-интерфейсы позволяют наилучшим образом контролировать производительность приложений, но это дается за счет дополнительного усложнения кода. Примерами функциональности, предоставляемой каркасами приложений, могут служить средства для работы в сети и синтаксические XML-анализаторы, имеющие несколько уровней абстракции, из которых разработчик может выбрать наиболее удобный для работы. При создании приложений всегда старайтесь использовать самый высокий уровень абстракции из тех, которые являются подходящими для решения стоящих перед вами задач.
Весьма показательным примером может служить работа с XML. Теоретически, разработчик может добиться абсолютного максимума производительности, выполняя синтаксический анализ необходимых данных путем непосредственного использования файловых потоков ввода-вывода. Однако такой подход будет крайне неразумным, чреватым ошибками и ничем не оправданным, если существуют хорошо спроектированные и проверенные высокоуровневые API-интерфейсы, которые позволяют выполнить данную задачу без заметного ущерба для производительности.
Идеальнее всего, если разработчику для загрузки и сохранения своих данных в виде XML-дерева удается использовать объектную модель документов (Document Object Model — DOM). Эта модель прекрасно подходит для работы с XML-данными среднего объема. Однако разработчики не должны забывать
о том, что XML DOM в значительной мере основана на использовании состояний; при загрузке XML-данных в память они в действительности сохраняются в памяти в виде дерева объектов, представляющих XML-документ. В случае крупных документов создание такого дерева может приводить к дефициту памяти. В противоположность этому сама модель XML DOM строится поверх классов XMLReader и XMLWriter, которые не имеют состояния и осуществляют лишь однонаправленный доступ к данным; эти классы осуществляют синтаксический анализ или генерируют XML-данные, основываясь на состоянии лишь в самой минимальной степени. Эти классы удерживают в памяти ровно столько информации, сколько необходимо для того, чтобы иметь возможность осуществлять разбор XML-данных или записывать их в поток; они и не генерируют, и не используют хранящиеся в памяти деревья данных.При работе с крупными XML-документами на мобильных устройствах с ограниченными ресурсами памяти наиболее подходящей является модель однонаправленного доступа к данным без сохранения состояния. Это остается справедливым даже при условии привлечения программистом низкоуровневых API-интерфейсов для синтаксического разбора XML-данных. Чтобы выбрать наиболее подходящий уровень абстракции API-интерфейса, необходимо хорошо себе представлять, какие объемы данных перемещаются и какие накладные расходы связаны с привлечением высокоуровневых абстракций.
Связь производительности с организацией пользовательского интерфейса и работы с графикой
Первые и самые глубокие впечатления от вашего приложения конечные пользователи получают при знакомстве с его пользовательским интерфейсом и графикой.
О приложении можно говорить, что оно имеет отличный внешний вид и прекрасный интерактивный интерфейс лишь постольку, поскольку таковым его воспринимают конечные пользователи. Для представления данных и взаимодействия с конечным пользователем существует практически бесчисленное количество способов, поэтому перед вами, как проектировщиком, простирается весьма широкий набор возможных для выбора вариантов. Такая ситуация может как вдохновлять, так и обескураживать вас, поэтому некоторые рекомендации здесь будут вполне уместными. При заполнении пользовательского интерфейса данными очень важно учитывать, какие данные могут действительно понадобиться пользователю, и не тратить время на разработку элементов управления с огромными списками или деревьями данных, до которых пользователь, скорее всего, не будет даже пытаться добраться; в противном случае вы зря потратите и время и ресурсы. Лучше затратьте это время на проектирование таких пользовательских интерфейсов, которые дают пользователю возможность легко переходить к нужным данным, и откладывайте вывод необязательных элементов управления пользовательского интерфейса до тех пор, пока в них не возникнет реальная необходимость. Возможно, прежде чем вы получите модель, которая вас устроит, вам придется много экспериментировать, поэтому будьте готовы к неоднократным переделкам интерфейса.
Вы должны тщательно исследовать пользовательский интерфейс, чтобы хорошо понимать тонкости его взаимодействия с кодом мобильного приложения. Особенно важно разобраться в том, как каркас пользовательского интерфейса активизирует код обработки событий приложения. Очень часто одного лишь просмотра кода еще не достаточно для того, чтобы с уверенностью сказать, когда именно будут запускаться те или иные события и каким образом код обработки событий взаимодействует со свойствами и методами, которые могут вызываться вашим приложением. Организация в коде средств контроля его выполнения и осуществление периодических проверок того, когда именно вызываются обработчики событий пользовательского интерфейса, помогут вам значительно глубже разобраться в характере взаимодействия каркаса этого интерфейса с вашим кодом.
Находящиеся в памяти битовые образы и сохраненные изображения — это та арена, на которой графика и пользовательский интерфейс взаимодействуют между собой. Сохраненные изображения часто удобно использовать в приложении либо в качестве части пользовательского интерфейса, либо в качестве данных. При использовании изображений очень важно контролировать их размеры, чтобы в целом они соответствовали характерным размерам интерфейса; изображения, размеры которых больше необходимых, требуют больших объемов памяти для хранения и используют значительно больше системной памяти в процессе загрузки. Вы должны отдельно исследовать вопрос о том, какой формат хранения изображений следует использовать, чтобы он был наиболее оптимальным для интересующей вас задачи. Для фотографических изображений самым употребительным форматом является JPG, тогда как для изображений, полученных методами компьютерной графики, — формат PNG.