Главная » Программирование игр под Android » ANDROIDLNPUT И ACCELEROMETERHANDLER

0

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

AccelerometerHandler: что сверху?

Начнем с самого простого из всех обработчиков – с AccelerometerHandler (листинг 5.5).

Листинг 5.5. AccelerometerHandler.Java; выполнение обработки, связанной с акселерометром.

Неудивительно, что данный класс применяет интерфейс SensorEventLi stener. Этот класс хранит три переменные, запоминая информацию об ускорении по каждой из трех осей акселерометра.

Конструктор принимает Context, из которого получает экземпляр класса SensorManager, чтобы начать слушать события. Обратите внимание, что если акселерометр отсутствует, то обработчик будет все время считать, что ускорение по всем осям равно нулю, а нам ни к чему дополнительная проверка на наличие ошибок или код для генерации исключений.

Следующие два метода – onAccuracyChangedO и onSensorChanged – также должны быть вам уже знакомы. В первом мы в данном случае ничего не делаем, так как здесь не о чем сообщать. Используя второй метод, мы выбираем данные акселерометра из принятого SensorEvent и сохраняем их в членах обработчика.

Последние три метода возвращают текущие значения ускорения по каждой из осей.

Обратите внимание, что мы не занимаемся здесь какой-либо синхронизацией, даже несмотря на то, что метод onSensorChanged может быть реализован в отдельном потоке. Модель памяти Java гарантирует, что операции записи и считывания из примитивных типов данных, таких как boolean, int или byte, являются атомарными. В данном случае можно положиться на этот факт, так как мы не делаем ничего сложного, а просто присваиваем новое значение. В другом случае, например, если бы мы оперировали переменными экземпляра в методе onSensorChangedC), нам понадобилась бы хорошая синхронизация. Класс Pool: используем повторно.

Какая самая ужасная вещь может случиться при программировании под Android? Несомненно, это всеостанавливающая сборка мусора. Если обратить внимание на определение интерфейса Input, то сразу будут заметны методы getTouchEventsC) и getKeyEvents. Они возвращают списки TouchEvents и KeyEvents. В наших обработчиках событий клавиатуры и сенсорного экрана мы будем постоянно создавать экземпляры этих двух классов и хранить их во внутренних списках обработчиков. Система ввода Android постоянно генерирует множество событий при касании экрана или нажатиях клавиш, поэтому нам пришлось бы непрерывно создавать новые экземпляры классов, которые подхватывались бы сборщиком мусора через небольшие промежутки времени. Чтобы этого избежать, реализуем концепцию, известную как пулинг экземпляров (instance pooling). Вместо того чтобы постоянно создавать новые экземпляры класса, мы просто будем использовать ранее созданные экземпляры. Класс Pool – это удобный способ реализовать подобное поведение программы. Рассмотрим его код в листинге 5.6.

Листинг 5.6. Pool.java; учимся обращаться со сборщиком мусора package com.badl ogi с.androi dgames.framework;

Теперь в ход пойдут дженерики: первая вещь, которую необходимо учесть, – то, что данный класс характеризуется обобщенной типизацией (generically typed) подобно классам коллекций, таким как ArrayLi st или HashMap. Дженерик позволяет хранить объект любого типа в нашем Pool, не тратя времени на лишние операции приведения. Так что же делает класс Pool ?

Для начала определим интерфейс Pool Ob jectFactory, который опять-таки является дженериком. Он использует только один метод, createObject, который возвращает новый обобщенный тип экземпляра класса Pool /PoolObjectFactory.

Класс Pool включает в себя три члена: ArrayList, сохраняющий объекты пула, PoolObjectFactory, который генерирует новые экземпляры типа, содержащегося в классе, и еще один член, хранящий максимальное количество объектов, которое может содержать в себе Pool. Последний элемент необходим для того, чтобы избежать неконтролируемого роста Pool, иначе может возникнуть исключение нехватки памяти.

Конструктор класса Pool принимает PoolObjectFactory и максимальное количество объектов, которое он может хранить. Мы сохраняем оба параметра в соответствующих переменных и создаем новый ArrayList, вместимость которого равна максимальному количеству объектов.

Метод newObject отвечает или за передачу нового экземпляра класса данного типа, который Pool получил через метод PoolObjectFactory.newObjectO, или за то, чтобы возвратить обобщенный экземпляр, если он уже есть, в freeObjects ArrayLi st. Если мы используем данный метод, то получим заново обработанные объекты при условии, что Pool уже хранит несколько таких объектов в списке freeObjects. В противном случае метод создаст новый объект с помощью фабрики.

Метод fгее позволяет переставлять объекты, которые мы больше не используем. Он просто вставляет объекты в список freeObjects, конечно, если тот не заполнен полностью. Если список полон, объект не будет добавлен. Скорее всего, его подберет сборщик мусора, когда будет запущен в следующий раз.

Как же мы можем использовать этот класс? Рассмотрим псевдокод, который показывает применение класса Pool при обработке касаний на экране:

Сначала определяем PoolObjectFactory, который создает экземпляры класса TouchEvent. Далее создаем Pool и указываем ему использовать нашу фабрику и одновременно хранить не более 50 событий TouchEvent. Когда нам нужен новый TouchEvent из Pool, используем метод newObject, относящийся к Pool. Изначально Pool пустой, поэтому он попросит фабрику создать новый экземпляр класса TouchEvent. Когда мы больше не нуждаемся в TouchEvent, мы можем вернуть его в Pool х с помощью метода Pool .freeO. В следующий раз, когда мы используем метод newObjectO, мы снова получим этот же экземпляр класса TouchEvent и вновь его задействуем. Таким образом, в данной ситуации мы обойдемся без сборщика мусора. Данный класс удобно применять во многих ситуациях. Просто запомните, что нужно быть внимательными, если вы многократно используете объекты. Если вы берете их из класса Pool, то их повторная инициализация, возможно, получится неполной, а этого допускать нельзя.

Источник: Mario Zechner / Марио Цехнер, «Программирование игр под Android», пер. Егор Сидорович, Евгений Зазноба, Издательство «Питер»

По теме:

  • Комментарии