Главная > Android > Использование общего объекта в нескольких активити

Использование общего объекта в нескольких активити

При написании программ часто возникает необходимость передачи данных между окнами, активити или представлениями. Например, в Android, чтобы передать простые данные между активити, рекомендуется использовать Extras. Однако возникает необходимость передавать не только простые переменные, но еще и объекты, да так, чтобы сам объект был общим для всех активити в приложении.

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


Реализация

Для решения этой задачи можно использовать класс Application - класс нашего приложения. Мы можем хранить в нем данные, которые требуется использовать на протяжении всей работы приложения, нужно только создать свой класс, наследованный от Application.

public class ApMy extends Application
{
 
}

Осталось не забыть прописать наш класс в манифесте.

<application android:icon="@drawable/icon"
             android:label="@string/app_name"
             android:name="ApMy">

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

private static final int COUNT = 2;
private static final int QUALITY = 0;
private static final int PRIORITY = 1;

Назначение этих констант рассмотрено ниже, в месте их использования.

Теперь необходимо глобально объявить требуемые объекты. В нашем случае это объект класса SoundPool и целочисленный массив SoundID, в котором будут храниться ID загруженных звуков.

private SoundPool soundPool;
private int[] soundID;

Чтобы иметь доступ к этим объектам, нужно для этого создать специальные функции в классе ApMy, которые будут возвращать указатели для дальнейшего использования в нужной нам активити.

Первая функция getSoundPool возвращает указатель на объект класса SoundPool:

public SoundPool getSoundPool()
{
    return soundPool;
}

Вторая getSoundID - указатель на целочисленный массив soundID:

public int[] getSoundID()
{
    return soundID;
}

Но чтобы было что возвращать, нужно провести инициализацию объектов. Я ее специально вынес в отдельную функцию initSound, чтобы была возможность организовать загрузку звуков в отдельном потоке, так как это довольно дорогостоящее по времени удовольствие.

Кстати, звуки boom.mp3 и bang.mp3 хранятся в папке res\raw\ нашего приложения (кроме mp3 можно использовать любой другой формат, который поддерживает Android).

public void initSound()
{
    soundPool = new SoundPool(COUNT, AudioManager.STREAM_MUSIC, QUALITY);
    soundID = new int[COUNT];
 
    soundID[0] = soundPool.load(getApplicationContext(), R.raw.boom, PRIORITY);
    soundID[1] = soundPool.load(getApplicationContext(), R.raw.bang, PRIORITY);
}

Немного подробней о передаваемых параметрах и константах.

При создании объекта класса SoundPool используется конструктор, в который первым параметром передается максимальное количество звуковых потоков, вторым - типа аудиопотока и, наконец, третьим - качество звука.

В массиве используется та же константа COUNT, что и в конструкторе SoundPool. Здесь она означает размер создаваемого массива. Сколько звуков в SoundPool, столько ID для их использования.

Функция load класса SoundPool первым параметром принимает контекст, вторым - сам звук (точней ID ресурса), который хранится в ресурсах и третьим - приоритет. Результатом выполнения этой функции является ID звука, который мы присваиваем соответствующему элементу массива soundID.

Кстати, на данный момент константы QUALITY и PRIORITY не имеют никакого эффекта, но для совместимости с будущими версиями Android рекомендуется использовать именно эти значения.

Как же теперь этим воспользоваться? А очень просто. Звуки нужно загрузить в самом начале программы с помощью функции initSound, при этом показав пользователю сообщение о том, что придется подождать. Затем в каждой активити, в которой нам потребуются звуки, будем просто присваивать местным объектам указатель на объект, созданный в ApMy. Для этого и будут использоваться функции getSoundPool и getSoundID

Допустим активити, в котором будет происходить загрузка звуков, называется AcMain.

public class AcMain extends Activity
{
 
}

Сразу объявим глобально объекты и константы, которые нам понадобятся:

//Номер нашего диалога, нужен для вызова диалога
private static final int DIALOG_PROGRESS = 0;
 
//Объект нашего приложения
private ApMy myApp;
 
//Поток, в котором будет происходить инициализация
private Runnable runnableInitSound;
 
//Диалог занятости, который будет показывать пользователю, что программа не зависла,
//а просто понадобилось некоторое время для загрузки ресурсов
private ProgressDialog progressDialog;

В наследуемом методе onCreate напишем инициализацию наших объектов. Начнем с создания самого метода. В нем загрузим интерфейс, описание которого находится в res\layout\ac_main.xml. Приводить его я не буду, так как содержимое особого значения не имеет.

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.ac_main);
}

Объект класса ApMy создается очень просто:

myApp = (ApMy) getApplicationContext();

А вот инициализация потока выглядит немого крупнее. На самом деле в нем мы только загружаем звуки в буфер, а после этого закрываем диалог ожидания.

runnableInitSound = new Runnable()
{
    @Override
    public void run()
    {
        myApp.initSound();
 
        progressDialog.dismiss();
    }
};

После всей инициализации в том же onCreate вызываем диалог ожидания.

showDialog(DIALOG_PROGRESS);

Осталось создать диалог и запустить поток загрузки звуков в буфер.

Для создания и отображения диалога, нам нужно переписать функцию onCreateDialog.

@Override
protected Dialog onCreateDialog(int id)
{
 
}

В качестве параметра эта функция принимает ID диалога (в нашем случае он занесен в константу DIALOG_PROGRESS), а возвращает созданный диалог. Для работы с определенным диалогом, рекомендуется использовать перебор (в нашем случае перебор будет состоять из одного варианта).

switch(id)
{
    case DIALOG_PROGRESS:
        //Создаем сам объект
        progressDialog = new ProgressDialog(this);
        //Выставляем сообщение, которое будет показываться пользователю
        progressDialog.setMessage("Идет загрузка звуков...");
        //Устанавливаем стиль диалога в "ожидающий"
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        //Запрещаем закрываться диалогу при нажатии на аппаратную кнопку "Отмена"
        progressDialog.setCancelable(false);
        //Показываем наш диалог пользователю
        progressDialog.show();
 
        //А здесь просто создаем поток для загрузки звуков и запускаем его
        Thread thread = new Thread(threadInitSound);
        thread.start();
 
        return progressDialog;
 
    default:
        return null;
}

Вот и все. Теперь можно в любой активити нашего приложения создавать объект класса ApMy и получать из него ссылки на объекты без повторной загрузки звуков в буфер.

  1. Пока что нет комментариев.
  1. Пока что нет уведомлений.