Статьи

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

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

В этой серии вы познакомитесь с полным процессом создания музыкального проигрывателя Android с нуля, но другим вариантом является использование одного из шаблонов приложений музыкального проигрывателя на Envato Market, например, музыкального проигрывателя Android , который позволяет пользователям просматривать и воспроизводить музыку по альбомам, исполнители, песни, плейлисты, папки и исполнители альбомов.

Музыкальный проигрыватель Android
Музыкальный проигрыватель Android

Или, для более индивидуальной помощи, вы можете нанять разработчика мобильных приложений в Envato Studio. Таким образом, вы можете получить надежную и доступную помощь по любым аспектам работы по разработке, которая вам не нравится, и сосредоточить свою энергию и время на областях, в которых вы сильнее.

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

Музыкальный проигрыватель Android

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

Добавьте новый класс в свое приложение, назвав его MusicService или другим именем по вашему выбору. Убедитесь, что оно совпадает с именем, указанным в манифесте. При создании класса в Eclipse выберите android.app.Service качестве его суперкласса. Затмение должно ввести схему:

1
2
3
4
5
6
7
8
9
public class MusicService extends Service {
 
  @Override
  public IBinder onBind(Intent arg0) {
    // TODO Auto-generated method stub
    return null;
  }
 
}

Расширьте начальную строку объявления класса, чтобы реализовать некоторые интерфейсы, которые мы будем использовать для воспроизведения музыки:

1
2
3
public class MusicService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {

Eclipse отобразит ошибку над именем класса. Наведите указатель мыши на ошибку и выберите « Добавить не реализованные методы» . Мы добавим код к методам через несколько минут. Интерфейсы, которые мы реализуем, помогут процессу взаимодействия с классом MediaPlayer .

Вашему классу также потребуется следующий дополнительный импорт:

1
2
3
4
5
6
7
8
import java.util.ArrayList;
import android.content.ContentUris;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.PowerManager;
import android.util.Log;

Добавьте следующие переменные экземпляра в новый класс Service :

1
2
3
4
5
6
//media player
private MediaPlayer player;
//song list
private ArrayList<Song> songs;
//current position
private int songPosn;

Мы передадим список песен в класс Service , воспроизводя его с помощью класса MediaPlayer и отслеживая положение текущей песни с помощью переменной экземпляра songPosn . Теперь onCreate метод onCreate для Service :

1
2
3
public void onCreate(){
  //create the service
}

Внутри onCreate вызовите метод суперкласса, MediaPlayer экземпляр переменных позиции и экземпляра MediaPlayer :

1
2
3
4
5
6
//create the service
super.onCreate();
//initialize position
songPosn=0;
//create player
player = new MediaPlayer();

Далее давайте добавим метод для инициализации класса MediaPlayer после метода onCreate :

1
2
3
public void initMusicPlayer(){
  //set player properties
}

Внутри этого метода мы настраиваем музыкальный проигрыватель, устанавливая некоторые его свойства, как показано ниже:

1
2
3
player.setWakeMode(getApplicationContext(),
  PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);

Функция wake lock позволит продолжить воспроизведение, когда устройство перейдет в режим ожидания, и мы установили тип потока для музыки. Установите класс в качестве прослушивателя для (1), когда подготовлен экземпляр MediaPlayer , (2), когда песня завершила воспроизведение, и когда (3) выдается ошибка:

1
2
3
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);

Обратите внимание, что они соответствуют интерфейсам, которые мы реализовали. Мы будем добавлять код в onPrepared , onCompletion и onError для ответа на эти события. Вернувшись в onCreate , вызовите initMusicPlayer :

1
initMusicPlayer();

Пришло время добавить метод в класс Service для передачи списка песен из Activity :

1
2
3
public void setList(ArrayList<Song> theSongs){
  songs=theSongs;
}

Мы будем вызывать этот метод позже в руководстве. Это станет частью взаимодействия между классами Activity и Service , для которого нам также понадобится экземпляр Binder . Добавьте следующий фрагмент в класс Service после метода setList :

1
2
3
4
5
public class MusicBinder extends Binder {
  MusicService getService() {
    return MusicService.this;
  }
}

Мы также получим доступ к этому из класса Activity .

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

1
2
3
4
5
6
7
import android.os.IBinder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.MenuItem;
import android.view.View;

И вам также нужно объявить три новые переменные экземпляра:

1
2
3
private MusicService musicSrv;
private Intent playIntent;
private boolean musicBound=false;

Мы собираемся воспроизводить музыку в классе Service , но управляем ею из класса Activity , где работает пользовательский интерфейс приложения. Для этого нам нужно будет привязаться к классу Service . Приведенные выше переменные экземпляра представляют класс Service и Intent , а также флаг для отслеживания того, связан ли класс Activity классом Service или нет. Добавьте следующее в ваш класс Activity после метода onCreate :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
//connect to the service
private ServiceConnection musicConnection = new ServiceConnection(){
 
  @Override
  public void onServiceConnected(ComponentName name, IBinder service) {
    MusicBinder binder = (MusicBinder)service;
    //get service
    musicSrv = binder.getService();
    //pass list
    musicSrv.setList(songList);
    musicBound = true;
  }
 
  @Override
  public void onServiceDisconnected(ComponentName name) {
    musicBound = false;
  }
};

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

1
import com.example.musicplayer.MusicService.MusicBinder;

Не забудьте изменить имена пакетов и классов, чтобы они соответствовали вашим собственным, если это необходимо.

Мы хотим запустить экземпляр Service при запуске экземпляра Activity , поэтому переопределите метод onStart :

1
2
3
4
5
6
7
8
9
@Override
protected void onStart() {
  super.onStart();
  if(playIntent==null){
    playIntent = new Intent(this, MusicService.class);
    bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
    startService(playIntent);
  }
}

Когда запускается экземпляр Activity , мы создаем объект Intent если он еще не существует, привязываемся к нему и запускаем его. Измените код, если вы выбрали другое имя для класса Service . Обратите внимание, что мы используем созданный нами объект подключения, чтобы при установлении подключения к связанному экземпляру Service мы пропускали список песен. Мы также сможем взаимодействовать с экземпляром Service , чтобы позже управлять воспроизведением.

Вернитесь в класс Service чтобы завершить этот процесс привязки. Добавьте переменную экземпляра, представляющую внутренний класс Binder мы добавили:

1
private final IBinder musicBind = new MusicBinder();

Теперь onBind метод onBind чтобы он возвращал этот объект:

1
2
3
4
@Override
public IBinder onBind(Intent intent) {
  return musicBind;
}

Добавьте метод onUnbind для освобождения ресурсов, когда экземпляр Service не связан:

1
2
3
4
5
6
@Override
public boolean onUnbind(Intent intent){
  player.stop();
  player.release();
  return false;
}

Это будет выполнено, когда пользователь выйдет из приложения, после чего мы остановим службу.

Давайте теперь настроим приложение для воспроизведения трека. В своем классе Service добавьте следующий метод:

1
2
3
public void playSong(){
  //play a song
}

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

1
player.reset();

Затем, возьмите песню из списка, извлеките ее идентификатор, используя ее объект Song , и смоделируйте ее как URI:

1
2
3
4
5
6
7
8
//get song
Song playSong = songs.get(songPosn);
//get id
long currSong = playSong.getID();
//set uri
Uri trackUri = ContentUris.withAppendedId(
  android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
  currSong);

Теперь мы можем попробовать установить этот URI в качестве источника данных для экземпляра MediaPlayer , но при возникновении ошибки может возникнуть исключение, поэтому мы используем блок try/catch :

1
2
3
4
5
6
try{
  player.setDataSource(getApplicationContext(), trackUri);
}
catch(Exception e){
  Log.e(«MUSIC SERVICE», «Error setting data source», e);
}

После блока catch завершите метод playSong , вызвав асинхронный метод MediaPlayer чтобы подготовить его:

1
player.prepareAsync();

Когда MediaPlayer подготовлен, будет выполнен метод onPrepared . Eclipse должен был вставить его в ваш класс Service . Внутри этого метода начните воспроизведение:

1
2
3
4
5
@Override
public void onPrepared(MediaPlayer mp) {
  //start playback
  mp.start();
}

Чтобы пользователь мог выбрать песни, нам также нужен метод в классе Service для установки текущей песни. Добавьте это сейчас:

1
2
3
public void setSong(int songIndex){
  songPosn=songIndex;
}

Мы назовем это, когда пользователь выберет песню из списка.

Помните, что мы добавили атрибут onClick в макет для каждого элемента в списке песен. Добавьте этот метод в основной класс Activity :

1
2
3
4
public void songPicked(View view){
  musicSrv.setSong(Integer.parseInt(view.getTag().toString()));
  musicSrv.playSong();
}

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

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

1
2
3
4
@Override
public boolean onOptionsItemSelected(MenuItem item) {
  //menu item selected
}

Внутри метода добавьте оператор switch для действий:

01
02
03
04
05
06
07
08
09
10
11
switch (item.getItemId()) {
case R.id.action_shuffle:
  //shuffle
  break;
case R.id.action_end:
  stopService(playIntent);
  musicSrv=null;
  System.exit(0);
  break;
}
return super.onOptionsItemSelected(item);

Мы реализуем функцию перемешивания в следующем уроке. Для кнопки завершения мы останавливаем экземпляр Service и закрываем приложение. Нажатие кнопки «Назад» не приведет к выходу из приложения, так как мы предполагаем, что пользователь хочет продолжить воспроизведение, если не выберет кнопку «Завершить». Используйте тот же процесс, если приложение уничтожено, переопределяя метод onDestroy :

1
2
3
4
5
6
@Override
protected void onDestroy() {
  stopService(playIntent);
  musicSrv=null;
  super.onDestroy();
}

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

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