Чтение онлайн

ЖАНРЫ

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

Салмре Иво

Шрифт:

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

Какой объем данных должен загружаться в каждый момент времени, чтобы пользователь имел возможность работать эффективно? 

Когда и при каких условиях следует освобождать память от загруженных ранее данных? 

Какой формат хранения данных в памяти является наиболее подходящим? При небольших объемах данных целесообразно выбирать тот формат, который более всего удобен с точки зрения программирования. С увеличением объема данных, например, при большом количестве строк, извлекаемых из базы данных, обязательным условием становится использование формата данных, оптимизированного в отношении расхода памяти и применяемых вычислительных методов. Кроме того, предусматриваемая модель должна обеспечивать сохранение в памяти недавно использованных данных и очистку памяти от всех остальных данных. 

Существуют

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

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

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

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

■ Глобально доступные кэшированные графические объекты. В нашей графической игре будет повторно выполняться множество самых обычных задач рисования. Вместо того чтобы непрерывно создавать и уничтожать часто используемые перья, кисти и растровые изображения, имеет смысл привлечь модель состояний, обеспечивающую хранение этих ресурсов в памяти на протяжении всего того времени, в течение которого они необходимы для выполнения повторяющихся задач перерисовки экрана. При переключении состояния игры в режим, в котором эти ресурсы не требуются, память может освобождаться от них.

Конечный автомат для фоновых задач

В ваших мобильных приложениях обязательно будут встречаться участки кода, которые либо долго выполняются, либо требуют сетевого доступа; в разряд "длительно выполняющихся" попадают все операции, выполнение которых может занимать более 0,5 секунды. Некоторые возможные примеры таких операций приводятся ниже: 

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

■ Загрузка или сохранение больших объемов данных. Загрузка нескольких сотен порций информации из XML-файла или базы данных может занимать довольно большое время. 

■ Сетевые операции. Почти все операции, выполняемые в сети, подвержены риску задержек и других проблем связи.

Операции подобного рода рекомендуется выполнять в асинхронном по отношению к потоку выполнения пользовательского интерфейса режиме. При реализации любой асинхронной операции конечный автомат, с помощью которого поток выполнения пользовательского интерфейса может связываться с фоновым потоком и получать информацию о ходе его выполнения, играет очень большую роль. Поток пользовательского интерфейса должен иметь возможность периодического опроса

состояния выполнения

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

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

Рис. 5.3. Конечный автомат, предназначенный для управления выполнением фонового алгоритма нахождения простых чисел

Стиль программирования: использование операторов goto и полных путей доступа в пространствах имен

Следует дать некоторые пояснения относительно двух аспектов стиля программирования, принятого во всех примерах кодов, приводимых в данной книге. 

Использование операторов goto

Вопрос о том, следует ли считать использование операторов goto в программах допустимым или же его необходимо рассматривать как признак плохого стиля программирования, является в определенной мере дискуссионным. Применимость операторов goto выступает предметом споров постольку, поскольку осуществляемая с их помощью передача управления противоречит принципам, лежащим в основе использования операторов условного перехода (if/then) и операторов циклов.

Лично я принадлежу к лагерю тех, кто считает использование операторов goto в программах вполне допустимым при условии выполнения следующих требований:

1) Им соответствует передача управления только в прямом направлении.

2) Они способствуют повышению удобочитаемости интересующих нас участков кода. Применение операторов goto может считаться непозволительным лишь в том случае, если это затрудняет понимание логики передачи управления в выполняющейся программе. Примером неудачного использования операторов goto может служить передача управления в различных направлениях, поскольку результатом этого является создание сложной системы замысловатых переходов внутри функций и трудности в отслеживании их логики; несомненно, любой из нас возражал бы против подобного использования этого оператора. 

Использование полных путей доступа в пространствах имен

Возможны два способа указания типов переменных в программах:

1) В теле программы можно указывать полный путь доступа. Например, оператор System.Threading.Thread newThread; объявляет переменную newThread типа System.Threading.Thread. Достоинством таких подробных объявлений является простота их применения.

2) В начале файла с текстом программы можно задавать использование определенного пространства имен, применяя для этого ключевое слово using языка C# или ключевое слово Imports языка Visual Basic .NET. Например, если в начале файла с программой на языке C# содержится оператор using SystemThreading;, то переменная System.Threading.Thread может быть объявлена просто как Thread newThread; без указания ее полного имени.

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

На заметку! Прежде чем вы сможете использовать те или иные классы и типы в своей программе, вы должны указать, к каким сборкам (assembly), они относятся. Очень важно, чтобы вы понимали, что ключевые слова using и Imports являются всего лишь удобным синтаксическим средством и не отменяют необходимости включения ссылки на сборку. Чтобы компилятору стали известны типы, содержащиеся в сборке, вы должны явно включить соответствующую ссылку в свою программу. В Visual Studio .NET список сборок можно просмотреть с помощью элемента управления TreeView в окне проводника решений Solution Explorer. Как правило, предварительно сконфигурированные часто используемые ссылки автоматически включаются во вновь создаваемые проекты.

Листинг 5.4. Код программы нахождения простых чисел, предназначенный для выполнения фоновым потоком 

using System;

public class FindNextPrimeNumber
{

 //Определить возможные состояния

 public enum ProcessingState {

notYetStarted,

waitingToStartAsync,

lookingForPrime,

foundPrime,

requestAbort,

Поделиться с друзьями: