В этой серии мы создаем музыкальный проигрыватель на Android с использованием классов MediaPlayer
и MediaController
. В первой части мы создали приложение и подготовили пользовательский интерфейс для воспроизведения. Мы представили список песен на пользовательском устройстве и указали способ выполнения, когда пользователь делает выбор. В этой части серии мы реализуем класс Service
для непрерывного воспроизведения музыки, даже когда пользователь не взаимодействует напрямую с приложением.
Ищете быстрое решение?
В этой серии вы познакомитесь с полным процессом создания музыкального проигрывателя Android с нуля, но другим вариантом является использование одного из шаблонов приложений музыкального проигрывателя на Envato Market, например, музыкального проигрывателя Android , который позволяет пользователям просматривать и воспроизводить музыку по альбомам, исполнители, песни, плейлисты, папки и исполнители альбомов.
Или, для более индивидуальной помощи, вы можете нанять разработчика мобильных приложений в Envato Studio. Таким образом, вы можете получить надежную и доступную помощь по любым аспектам работы по разработке, которая вам не нравится, и сосредоточить свою энергию и время на областях, в которых вы сильнее.
Вступление
Нам понадобится приложение для привязки к Сервису воспроизведения музыки, чтобы взаимодействовать с воспроизведением, поэтому вы изучите некоторые из основных аспектов жизненного цикла Service
в этом руководстве, если вы не изучали их ранее. Вот предварительный просмотр окончательного результата, к которому мы стремимся:
В последней части этой серии мы добавим пользовательский контроль над воспроизведением, а также позаботимся о том, чтобы приложение продолжало функционировать в различных состояниях приложения. Позже мы продолжим серию с дополнительными улучшениями, которые вы, возможно, пожелаете добавить, такими как управление фокусировкой звука, воспроизведение видео и потокового мультимедиа, а также альтернативные способы представления мультимедийных файлов.
1. Создать сервис
Шаг 1
Добавьте новый класс в свое приложение, назвав его 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;
|
Шаг 2
Добавьте следующие переменные экземпляра в новый класс 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();
|
Шаг 3
Пришло время добавить метод в класс 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
.
2. Запустите Сервис
Шаг 1
Вернувшись в основной класс 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;
|
Не забудьте изменить имена пакетов и классов, чтобы они соответствовали вашим собственным, если это необходимо.
Шаг 2
Мы хотим запустить экземпляр 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;
}
|
Это будет выполнено, когда пользователь выйдет из приложения, после чего мы остановим службу.
3. Начните воспроизведение
Шаг 1
Давайте теперь настроим приложение для воспроизведения трека. В своем классе 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();
|
Шаг 2
Когда 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;
}
|
Мы назовем это, когда пользователь выберет песню из списка.
Шаг 3
Помните, что мы добавили атрибут 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();
}
|
Когда вы запустите приложение на этом этапе, вы сможете воспроизводить песни, выбрав их из списка, и вы также можете выйти из приложения с помощью кнопки завершения .
Вывод
Теперь мы реализовали базовое воспроизведение музыкальных треков, выбранных из списка музыкальных файлов пользователя. В заключительной части этой серии мы добавим медиа-контроллер, с помощью которого пользователь сможет управлять воспроизведением. Мы добавим уведомление, чтобы пользователь мог вернуться в приложение после перехода от него, и мы проведем некоторую домашнюю работу, чтобы приложение справилось с различными действиями пользователя.