Статьи

Фоновое аудио в Android с MediaSessionCompat

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

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

Первое, что вам нужно сделать, это включить библиотеку поддержки Android в ваш проект. Это можно сделать, добавив следующую строку в файл build.gradle вашего модуля под узлом зависимостей.

1
compile ‘com.android.support:support-v13:24.2.1’

После синхронизации проекта создайте новый класс Java. Для этого примера я назову класс BackgroundAudioService . Этот класс должен будет расширить MediaBrowserServiceCompat . Мы также будем реализовывать следующие интерфейсы: MediaPlayer.OnCompletionListener и AudioManager.OnAudioFocusChangeListener .

Теперь, когда ваша реализация MediaBrowserServiceCompat создана, давайте MediaBrowserServiceCompat время обновлению AndroidManifest.xml, прежде чем вернуться в этот класс. В верхней части класса вам нужно будет запросить разрешение WAKE_LOCK .

1
<uses-permission android:name=»android.permission.WAKE_LOCK» />

Затем в узле application объявите новую службу со следующими элементами intent-filter . Это позволит вашей службе перехватывать кнопки управления, события в наушниках и просмотр мультимедиа для таких устройств, как Android Auto (хотя мы не будем ничего делать с Android Auto для этого учебного пособия, MediaBrowserServiceCompat по-прежнему требует некоторой базовой поддержки для него).

1
2
3
4
5
6
7
<service android:name=».BackgroundAudioService»>
    <intent-filter>
        <action android:name=»android.intent.action.MEDIA_BUTTON» />
        <action android:name=»android.media.AUDIO_BECOMING_NOISY» />
        <action android:name=»android.media.browse.MediaBrowserService» />
    </intent-filter>
</service>

Наконец, вам нужно будет объявить об использовании MediaButtonReceiver из библиотеки поддержки Android. Это позволит вам перехватывать взаимодействия кнопок управления мультимедиа и события в наушниках на устройствах под управлением KitKat и более ранних версий.

1
2
3
4
5
6
<receiver android:name=»android.support.v4.media.session.MediaButtonReceiver»>
    <intent-filter>
        <action android:name=»android.intent.action.MEDIA_BUTTON» />
        <action android:name=»android.media.AUDIO_BECOMING_NOISY» />
    </intent-filter>
</receiver>

Теперь, когда ваш файл AndroidManifest.xml завершен, вы можете закрыть его. Мы также собираемся создать еще один класс с именем MediaStyleHelper , который был написан Ианом Лейком , разработчиком Google в Google, для очистки создания уведомлений в стиле медиа.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class MediaStyleHelper {
    /**
     * Build a notification using the information from the given media session.
     * of {@link MediaMetadataCompat#getDescription()} to extract the appropriate information.
     * @param context Context used to construct the notification.
     * @param mediaSession Media session to get information.
     * @return A pre-built notification with information from the given media session.
     */
    public static NotificationCompat.Builder from(
            Context context, MediaSessionCompat mediaSession) {
        MediaControllerCompat controller = mediaSession.getController();
        MediaMetadataCompat mediaMetadata = controller.getMetadata();
        MediaDescriptionCompat description = mediaMetadata.getDescription();
 
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
        builder
                .setContentTitle(description.getTitle())
                .setContentText(description.getSubtitle())
                .setSubText(description.getDescription())
                .setLargeIcon(description.getIconBitmap())
                .setContentIntent(controller.getSessionActivity())
                .setDeleteIntent(
                        MediaButtonReceiver.buildMediaButtonPendingIntent(context, PlaybackStateCompat.ACTION_STOP))
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
        return builder;
    }
}

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

Теперь пришло время углубиться в суть создания медиа-приложения. Существует несколько переменных-членов, которые вы хотите объявить первыми для этого примера приложения: MediaPlayer для фактического воспроизведения и объект MediaSessionCompat который будет управлять метаданными и элементами управления / состояниями воспроизведения.

1
2
private MediaPlayer mMediaPlayer;
private MediaSessionCompat mMediaSessionCompat;

Кроме того, вам потребуется BroadcastReceiver который прослушивает изменения состояния наушников. Для простоты этот ресивер приостановит MediaPlayer , если он воспроизводит.

1
2
3
4
5
6
7
8
private BroadcastReceiver mNoisyReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if( mMediaPlayer != null && mMediaPlayer.isPlaying() ) {
            mMediaPlayer.pause();
        }
    }
};

Для конечной переменной-члена вы MediaSessionCompat.Callback объект MediaSessionCompat.Callback , который используется для обработки состояния воспроизведения при выполнении действий мультимедийного сеанса.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback() {
     
    @Override
    public void onPlay() {
        super.onPlay();
    }
 
    @Override
    public void onPause() {
        super.onPause();
    }
 
    @Override
    public void onPlayFromMediaId(String mediaId, Bundle extras) {
        super.onPlayFromMediaId(mediaId, extras);
    }
};

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@Nullable
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
    if(TextUtils.equals(clientPackageName, getPackageName())) {
        return new BrowserRoot(getString(R.string.app_name), null);
    }
 
    return null;
}
 
//Not important for general audio service, required for class
@Override
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
    result.sendResult(null);
}

Наконец, вы захотите переопределить метод onStartCommand() , который является точкой входа в ваш Service . Этот метод возьмет Намерение, переданное MediaButtonReceiver и отправит его в класс MediaButtonReceiver .

1
2
3
4
5
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent);
    return super.onStartCommand(intent, flags, startId);
}

Теперь, когда ваши базовые переменные-члены созданы, пришло время все инициализировать. Мы сделаем это, вызвав различные вспомогательные методы в onCreate() .

1
2
3
4
5
6
7
8
@Override
public void onCreate() {
    super.onCreate();
 
    initMediaPlayer();
    initMediaSession();
    initNoisyReceiver();
}

Первый метод, initMediaPlayer() , инициализирует объект MediaPlayer который мы создали в верхней части класса, запрашивает частичную блокировку после пробуждения (именно поэтому нам требуется это разрешение в AndroidManifest.xml ) и устанавливает громкость проигрывателя.

1
2
3
4
5
6
private void initMediaPlayer() {
    mMediaPlayer = new MediaPlayer();
    mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mMediaPlayer.setVolume(1.0f, 1.0f);
}

Следующий метод initMediaSession() — это место, где мы инициализируем объект MediaSessionCompat и MediaSessionCompat его с мультимедийными кнопками и методами управления, которые позволяют нам обрабатывать воспроизведение и ввод данных пользователем. Этот метод начинается с создания объекта ComponentName который указывает на класс MediaButtonReceiver библиотеки поддержки Android, и использует его для создания нового MediaSessionCompat . Затем мы передаем MediaSession.Callback объект MediaSession.Callback который мы создали ранее, и устанавливаем флаги, необходимые для получения входных сигналов мультимедийных кнопок и сигналов управления. Затем мы создаем новое Intent для обработки ввода мультимедийных кнопок на устройствах, предшествующих Lollipop, и устанавливаем токен мультимедийного сеанса для нашего сервиса.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
private void initMediaSession() {
    ComponentName mediaButtonReceiver = new ComponentName(getApplicationContext(), MediaButtonReceiver.class);
    mMediaSessionCompat = new MediaSessionCompat(getApplicationContext(), «Tag», mediaButtonReceiver, null);
 
    mMediaSessionCompat.setCallback(mMediaSessionCallback);
    mMediaSessionCompat.setFlags( MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS );
 
    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    mediaButtonIntent.setClass(this, MediaButtonReceiver.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
    mMediaSessionCompat.setMediaButtonReceiver(pendingIntent);
 
    setSessionToken(mMediaSessionCompat.getSessionToken());
}

Наконец, мы зарегистрируем созданный нами BroadcastReceiver в верхней части класса, чтобы мы могли прослушивать события смены наушников.

1
2
3
4
5
private void initNoisyReceiver() {
    //Handles headphones coming unplugged.
    IntentFilter filter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
    registerReceiver(mNoisyReceiver, filter);
}

Теперь, когда вы закончили инициализацию объектов BroadcastReceiver , MediaSessionCompat и MediaPlayer , пришло время изучить обработку фокуса аудио.

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

Первый случай, который мы хотим рассмотреть, — это запуск воспроизведения и попытка получить фокус устройства. В вашем объекте MediaSessionCompat.Callback перейдите в метод onPlay() и добавьте следующую проверку условий.

1
2
3
4
5
6
7
@Override
public void onPlay() {
    super.onPlay();
    if( !successfullyRetrievedAudioFocus() ) {
        return;
    }
}

Приведенный выше код вызовет вспомогательный метод, который пытается получить фокус, а если это невозможно, он просто вернется. В реальном приложении вы бы хотели обработать неудачное воспроизведение аудио более изящно. AudioManager successfullyRetrievedAudioFocus() получит ссылку на систему AudioManager и попытается запросить аудио-фокус для потоковой передачи музыки. Затем он вернет boolean представляющее, успешно ли выполнен запрос.

1
2
3
4
5
6
7
8
private boolean successfullyRetrievedAudioFocus() {
    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
 
    int result = audioManager.requestAudioFocus(this,
            AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
     
    return result == AudioManager.AUDIOFOCUS_GAIN;
}

Вы заметите, что мы также передаем this в метод requestAudioFocus() , который связывает OnAudioFocusChangeListener с нашим сервисом. Есть несколько разных состояний, которые вы захотите выслушать, чтобы быть «хорошим гражданином» в экосистеме приложений устройства.

  • AudioManager.AUDIOFOCUS_LOSS : это происходит, когда другое приложение запросило фокусировку звука. Когда это происходит, вы должны остановить воспроизведение звука в вашем приложении.
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT : это состояние вводится, когда другое приложение хочет воспроизвести аудио, но оно только ожидает, что потребуется фокусировка в течение короткого времени. Вы можете использовать это состояние, чтобы приостановить воспроизведение звука.
  • AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK : Когда запрашивается фокусировка звука, но выбрасывается состояние «может пригнуться», это означает, что вы можете продолжить воспроизведение, но немного понизить громкость. Это может произойти, когда устройство воспроизводит звук уведомления.
  • AudioManager.AUDIOFOCUS_GAIN : Последнее состояние, которое мы обсудим, — AUDIOFOCUS_GAIN . Это состояние, когда воспроизводимое аудио воспроизведение завершено, и ваше приложение может возобновить работу на прежних уровнях.

Упрощенный onAudioFocusChange() вызов onAudioFocusChange() может выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
public void onAudioFocusChange(int focusChange) {
    switch( focusChange ) {
        case AudioManager.AUDIOFOCUS_LOSS: {
            if( mMediaPlayer.isPlaying() ) {
                mMediaPlayer.stop();
            }
            break;
        }
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: {
            mMediaPlayer.pause();
            break;
        }
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: {
            if( mMediaPlayer != null ) {
                mMediaPlayer.setVolume(0.3f, 0.3f);
            }
            break;
        }
        case AudioManager.AUDIOFOCUS_GAIN: {
            if( mMediaPlayer != null ) {
                if( !mMediaPlayer.isPlaying() ) {
                    mMediaPlayer.start();
                }
                mMediaPlayer.setVolume(1.0f, 1.0f);
            }
            break;
        }
    }
}

Теперь, когда у вас есть общая структура для вашего Service , пришло время погрузиться в MediaSessionCompat.Callback . В последнем разделе вы добавили немного в onPlay() чтобы проверить, был ли предоставлен аудио-фокус. Ниже условного оператора вы хотите установить MediaSessionCompat объект MediaSessionCompat , присвоить ему состояние STATE_PLAYING и назначить надлежащие действия, необходимые для создания кнопок паузы в элементах управления экрана блокировки перед STATE_PLAYING на STATE_PLAYING , STATE_PLAYING об STATE_PLAYING телефона и Android.

01
02
03
04
05
06
07
08
09
10
11
12
@Override
public void onPlay() {
    super.onPlay();
    if( !successfullyRetrievedAudioFocus() ) {
        return;
    }
 
    mMediaSessionCompat.setActive(true);
    setMediaPlaybackState(PlaybackStateCompat.STATE_PLAYING);
     
    …
}

setMediaPlaybackState() метод setMediaPlaybackState() является вспомогательным методом, который создает объект PlaybackStateCompat.Builder и присваивает ему правильные действия и состояние, а затем создает и связывает PlaybackStateCompat с вашим объектом MediaSessionCompat .

01
02
03
04
05
06
07
08
09
10
private void setMediaPlaybackState(int state) {
    PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder();
    if( state == PlaybackStateCompat.STATE_PLAYING ) {
        playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE);
    } else {
        playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY);
    }
    playbackstateBuilder.setState(state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
    mMediaSessionCompat.setPlaybackState(playbackstateBuilder.build());
}

Важно отметить, что вам понадобятся ACTION_PLAY_PAUSE ACTION_PAUSE а ACTION_PLAY флаги ACTION_PAUSE или ACTION_PLAY в ваших действиях, чтобы получить надлежащие элементы управления Android Wear.

Медиа-уведомление на Android Wear

Вернувшись в onPlay() , вы захотите показать уведомление о воспроизведении, которое связано с вашим объектом MediaSessionCompat с помощью класса MediaStyleHelper который мы определили ранее, а затем показать это уведомление.

01
02
03
04
05
06
07
08
09
10
11
12
private void showPlayingNotification() {
    NotificationCompat.Builder builder = MediaStyleHelper.from(BackgroundAudioService.this, mMediaSessionCompat);
    if( builder == null ) {
        return;
    }
 
 
    builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_pause, «Pause», MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE)));
    builder.setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0).setMediaSession(mMediaSessionCompat.getSessionToken()));
    builder.setSmallIcon(R.mipmap.ic_launcher);
    NotificationManagerCompat.from(BackgroundAudioService.this).notify(1, builder.build());
}

Наконец, вы запустите MediaPlayer в конце onPlay() .

1
2
3
4
5
6
7
8
9
@Override
public void onPlay() {
    super.onPlay();
 
    …
 
    showPlayingNotification();
    mMediaPlayer.start();
}
Уведомление об управлении мультимедиа на устройстве Android Nougat

Когда обратный вызов получает команду паузы, onPause() . Здесь вы будете приостанавливать MediaPlayer , устанавливать состояние STATE_PAUSED и показывать приостановленное уведомление.

01
02
03
04
05
06
07
08
09
10
@Override
public void onPause() {
    super.onPause();
 
    if( mMediaPlayer.isPlaying() ) {
        mMediaPlayer.pause();
        setMediaPlaybackState(PlaybackStateCompat.STATE_PAUSED);
        showPausedNotification();
    }
}

Наш вспомогательный метод showPausedNotification() будет выглядеть аналогично showPlayNotification() .

01
02
03
04
05
06
07
08
09
10
11
private void showPausedNotification() {
    NotificationCompat.Builder builder = MediaStyleHelper.from(this, mMediaSessionCompat);
    if( builder == null ) {
        return;
    }
 
    builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_play, «Play», MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE)));
    builder.setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0).setMediaSession(mMediaSessionCompat.getSessionToken()));
    builder.setSmallIcon(R.mipmap.ic_launcher);
    NotificationManagerCompat.from(this).notify(1, builder.build());
}

Следующий метод в onPlayFromMediaId() который мы обсудим, onPlayFromMediaId() , принимает в качестве параметров String и Bundle . Это метод обратного вызова, который вы можете использовать для изменения звуковых дорожек / содержимого в вашем приложении.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
    super.onPlayFromMediaId(mediaId, extras);
 
    try {
        AssetFileDescriptor afd = getResources().openRawResourceFd(Integer.valueOf(mediaId));
        if( afd == null ) {
            return;
        }
 
        try {
            mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
 
        } catch( IllegalStateException e ) {
            mMediaPlayer.release();
            initMediaPlayer();
            mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
        }
 
        afd.close();
        initMediaSessionMetadata();
 
    } catch (IOException e) {
        return;
    }
 
    try {
        mMediaPlayer.prepare();
    } catch (IOException e) {}
 
    //Work with extras here if you want
}

Теперь, когда мы обсудили два основных метода в этом обратном вызове, которые вы будете использовать в своих приложениях, важно знать, что есть другие дополнительные методы, которые вы можете использовать для настройки вашего сервиса. Некоторые методы включают onSeekTo() , который позволяет вам изменить позицию воспроизведения вашего контента, и onCommand() , который примет String обозначающий тип команды, Bundle для дополнительной информации о команде и ResultReceiver вызов ResultReceiver , который позволит вам отправлять пользовательские команды на ваш Service .

01
02
03
04
05
06
07
08
09
10
11
12
@Override
public void onCommand(String command, Bundle extras, ResultReceiver cb) {
    super.onCommand(command, extras, cb);
    if( COMMAND_EXAMPLE.equalsIgnoreCase(command) ) {
        //Custom command here
    }
}
 
@Override
public void onSeekTo(long pos) {
    super.onSeekTo(pos);
}

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

1
2
3
4
5
6
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
    if( mMediaPlayer != null ) {
        mMediaPlayer.release();
    }
}

Наконец, мы хотим сделать несколько вещей в onDestroy() нашего Service . Во-первых, получите ссылку на AudioManager системной службы и в качестве параметра вызовите abandonAudioFocus() с нашим AudioFocusChangeListener , который уведомит другие приложения на устройстве о том, что вы отказываетесь от фокусировки звука. Затем MediaSessionCompat регистрацию BroadcastReceiver который был настроен для прослушивания изменений в наушниках, и освободите объект MediaSessionCompat . Наконец, вы хотите отменить уведомление управления воспроизведением.

1
2
3
4
5
6
7
8
9
@Override
public void onDestroy() {
    super.onDestroy();
    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    audioManager.abandonAudioFocus(this);
    unregisterReceiver(mNoisyReceiver);
    mMediaSessionCompat.release();
    NotificationManagerCompat.from(this).cancel(1);
}

На этом этапе у вас должна быть работающая базовая Service фонового звука, использующая MediaSessionCompat для управления воспроизведением на разных устройствах. Несмотря на то, что уже было приложено много усилий для создания службы, вы должны иметь возможность управлять воспроизведением из своего приложения, уведомлений, элементов управления экраном блокировки на устройствах, предшествующих Lollipop (Lollipop и более поздние версии будут использовать уведомление на экране блокировки), и с периферийных устройств, таких как Android Wear, после запуска Service .

Элементы управления экраном блокировки мультимедиа на Android Kit Kat

Хотя большинство элементов управления будут автоматическими, у вас все еще будет немного работы для запуска и управления сеансом мультимедиа из элементов управления в приложении. По крайней мере, вам понадобятся MediaBrowserCompat.ConnectionCallback , MediaControllerCompat.Callback , MediaBrowserCompat и MediaControllerCompat созданные в вашем приложении.

MediaControllerCompat.Callback будет иметь метод onPlaybackStateChanged() который получает изменения в состоянии воспроизведения и может использоваться для синхронизации вашего пользовательского интерфейса.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
private MediaControllerCompat.Callback mMediaControllerCompatCallback = new MediaControllerCompat.Callback() {
 
    @Override
    public void onPlaybackStateChanged(PlaybackStateCompat state) {
        super.onPlaybackStateChanged(state);
        if( state == null ) {
            return;
        }
         
        switch( state.getState() ) {
            case PlaybackStateCompat.STATE_PLAYING: {
                mCurrentState = STATE_PLAYING;
                break;
            }
            case PlaybackStateCompat.STATE_PAUSED: {
                mCurrentState = STATE_PAUSED;
                break;
            }
        }
    }
};

MediaBrowserCompat.ConnectionCallback имеет метод onConnected() который будет вызываться при создании и подключении нового объекта MediaBrowserCompat . Вы можете использовать это для инициализации объекта MediaControllerCompat , связать его с MediaControllerCompat.Callback и связать его с MediaSessionCompat из вашей Service . После этого вы можете начать воспроизведение аудио с помощью этого метода.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private MediaBrowserCompat.ConnectionCallback mMediaBrowserCompatConnectionCallback = new MediaBrowserCompat.ConnectionCallback() {
 
    @Override
    public void onConnected() {
        super.onConnected();
        try {
            mMediaControllerCompat = new MediaControllerCompat(MainActivity.this, mMediaBrowserCompat.getSessionToken());
            mMediaControllerCompat.registerCallback(mMediaControllerCompatCallback);
            setSupportMediaController(mMediaControllerCompat);
            getSupportMediaController().getTransportControls().playFromMediaId(String.valueOf(R.raw.warner_tautz_off_broadway), null);
 
        } catch( RemoteException e ) {
 
        }
    }
};

Вы заметите, что приведенный выше фрагмент кода использует getSupportMediaController().getTransportControls() для связи с медиа-сессией. Используя ту же технику, вы можете вызывать onPlay() и onPause() в объекте MediaSessionCompat.Callback вашего аудио сервиса.

01
02
03
04
05
06
07
08
09
10
if( mCurrentState == STATE_PAUSED ) {
    getSupportMediaController().getTransportControls().play();
    mCurrentState = STATE_PLAYING;
} else {
    if( getSupportMediaController().getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING ) {
        getSupportMediaController().getTransportControls().pause();
    }
 
    mCurrentState = STATE_PAUSED;
}

Когда вы закончите воспроизведение аудио, вы можете приостановить аудиосервис и отключить объект MediaBrowserCompat , что мы и сделаем в этом руководстве, когда это действие будет уничтожено.

1
2
3
4
5
6
7
8
9
@Override
protected void onDestroy() {
    super.onDestroy();
    if( getSupportMediaController().getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING ) {
        getSupportMediaController().getTransportControls().pause();
    }
 
    mMediaBrowserCompat.disconnect();
}

Уф! Как вы можете видеть, есть много движущихся частей, связанных с созданием и использованием фонового аудио сервиса правильно.

В этом руководстве вы создали Службу, которая воспроизводит простой аудиофайл, прослушивает изменения в фокусировке звука и ссылается на MediaSessionCompat чтобы обеспечить универсальное управление воспроизведением на устройствах Android, включая телефоны и Android Wear. Если вы столкнулись с препятствиями во время работы с этим учебником, я настоятельно рекомендую проверить соответствующий код проекта Android на GitHub Envato Tuts +.

И ознакомьтесь с некоторыми другими нашими курсами и руководствами по Android здесь на Envato Tuts +!

  • Android SDK
    Что нового в Android N и Wear 2.0
  • Android SDK
    Сервисы Google Play: Google Cast v3 и Media
  • Android SDK
    Делайте фотографии с помощью приложения для Android
    Ашраф Хатхибелагал