Często zdarza się, że chcemy odtwarzać multimedia, gdy aplikacja nie jest na pierwszym planie. Na przykład odtwarzacz muzyki zwykle odtwarza muzykę, gdy użytkownik zablokuje urządzenie lub użyje innej aplikacji. Biblioteka Media3 zawiera serię interfejsów, które umożliwiają obsługę odtwarzania w tle.
Korzystanie z MediaSessionService
Aby włączyć odtwarzanie w tle, musisz umieścić Player
i MediaSession
w osobnej usłudze.
Dzięki temu urządzenie będzie nadal wyświetlać treści multimedialne, nawet gdy aplikacja nie jest na pierwszym planie.

MediaSessionService
umożliwia prowadzenie sesji multimedialnej niezależnie od działania aplikacjiPodczas hostowania odtwarzacza w ramach Usługi należy użyć MediaSessionService
.
Aby to zrobić, utwórz klasę, która rozszerza klasę MediaSessionService
, i w jej ramach utwórz sesję multimediów.
Dzięki MediaSessionService
klienci zewnętrzni, np. Asystent Google, systemowe elementy sterujące multimediami, przyciski multimedialne na urządzeniach peryferyjnych czy urządzenia towarzyszące, takie jak Wear OS, mogą wykrywać Twoją usługę, łączyć się z nią i sterować odtwarzaniem bez konieczności korzystania z interfejsu użytkownika aplikacji. W tym samym czasie do tego samego MediaSessionService
może być podłączonych kilka aplikacji klienckich, z których każda ma swój własny MediaController
.
Wdrażanie cyklu życia usługi
Musisz zaimplementować 2 metody cyklu życia usługi:
onCreate()
jest wywoływany, gdy pierwszy kontroler ma się połączyć, a usługa jest tworzona i uruchamiana. To najlepsze miejsce do tworzeniaPlayer
iMediaSession
.- Funkcja
onDestroy()
jest wywoływana, gdy usługa jest zatrzymywana. Wszystkie zasoby, w tym odtwarzacz i sesja, muszą zostać zwolnione.
Opcjonalnie możesz zastąpić wartość onTaskRemoved(Intent)
, aby dostosować to, co dzieje się, gdy użytkownik zamknie aplikację z ostatnich zadań. Domyślnie usługa jest uruchomiona, jeśli odtwarzanie jest w toku, a w przeciwnym razie zostaje zatrzymana.
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your player and media session in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
Java
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; // Create your Player and MediaSession in the onCreate lifecycle event @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } // Remember to release the player and media session in onDestroy @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
Zamiast utrzymywać odtwarzanie w tle możesz w każdym przypadku zatrzymać usługę, gdy użytkownik zamknie aplikację:
Kotlin
override fun onTaskRemoved(rootIntent: Intent?) { pauseAllPlayersAndStopSelf() }
Java
@Override public void onTaskRemoved(@Nullable Intent rootIntent) { pauseAllPlayersAndStopSelf(); }
W przypadku dowolnej innej ręcznej implementacji onTaskRemoved
możesz użyć funkcji isPlaybackOngoing()
, aby sprawdzić, czy odtwarzanie jest uważane za trwające, a usługa na pierwszym planie jest uruchomiona.
Przyznawanie dostępu do sesji multimediów
Zastąpić metodę onGetSession()
, aby umożliwić innym klientom dostęp do sesji multimediów utworzonej podczas tworzenia usługi.
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // [...] lifecycle methods omitted override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = mediaSession }
Java
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; // [...] lifecycle methods omitted @Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { return mediaSession; } }
Zadeklaruj usługę w pliku manifestu
Aplikacja wymaga uprawnień FOREGROUND_SERVICE
i FOREGROUND_SERVICE_MEDIA_PLAYBACK
do uruchamiania usługi odtwarzania na pierwszym planie:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
Musisz też zadeklarować klasę Service
w pliku manifestu za pomocą filtra intencji MediaSessionService
i elementu foregroundServiceType
zawierającego mediaPlayback
.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
sterować odtwarzaniem za pomocą MediaController
,
W aktywności lub fragmencie zawierającym interfejs użytkownika odtwarzacza możesz utworzyć link między interfejsem a sesją multimediów za pomocą elementu MediaController
. Twój interfejs użytkownika używa sterownika multimediów, aby wysyłać polecenia z interfejsu do odtwarzacza w trakcie sesji. Szczegółowe informacje o tworzeniu i używaniu MediaController
znajdziesz w przewodniku Tworzenie MediaController
.
Obsługa poleceń MediaController
MediaSession
otrzymuje polecenia od kontrolera za pomocą MediaSession.Callback
. Inicjowanie MediaSession
tworzy domyślną implementację MediaSession.Callback
, która automatycznie obsługuje wszystkie polecenia wysyłane przez MediaController
do odtwarzacza.
Powiadomienie
MediaSessionService
automatycznie tworzy dla Ciebie MediaNotification
, który powinien działać w większości przypadków. Domyślnie opublikowane powiadomienie to powiadomienie MediaStyle
, które jest aktualizowane o najnowsze informacje z sesji multimediów i wyświetla elementy sterujące odtwarzaniem. MediaNotification
wie, że korzystasz z sesji, i możesz z niego sterować odtwarzaniem w innych aplikacjach połączonych z tą samą sesją.
Na przykład aplikacja do strumieniowego odtwarzania muzyki korzystająca z elementu MediaSessionService
utworzy element MediaNotification
, który wyświetla tytuł, wykonawcę i okładkę albumu dla aktualnie odtwarzanego elementu multimedialnego wraz z elementami sterującymi odtwarzaniem na podstawie konfiguracji elementu MediaSession
.
Wymagane metadane można podać w multimediów lub zadeklarować jako część elementu multimediów, jak w tym fragmencie kodu:
Kotlin
val mediaItem = MediaItem.Builder() .setMediaId("media-1") .setUri(mediaUri) .setMediaMetadata( MediaMetadata.Builder() .setArtist("David Bowie") .setTitle("Heroes") .setArtworkUri(artworkUri) .build() ) .build() mediaController.setMediaItem(mediaItem) mediaController.prepare() mediaController.play()
Java
MediaItem mediaItem = new MediaItem.Builder() .setMediaId("media-1") .setUri(mediaUri) .setMediaMetadata( new MediaMetadata.Builder() .setArtist("David Bowie") .setTitle("Heroes") .setArtworkUri(artworkUri) .build()) .build(); mediaController.setMediaItem(mediaItem); mediaController.prepare(); mediaController.play();
Cykl życia powiadomienia
Powiadomienie jest tworzone, gdy Player
ma MediaItem
występów na playliście.
Wszystkie aktualizacje powiadomień są przeprowadzane automatycznie na podstawie stanu Player
i MediaSession
.
Powiadomienia nie można usunąć, gdy usługa na pierwszym planie jest uruchomiona. Aby natychmiast usunąć powiadomienie, musisz wywołać funkcję Player.release()
lub wyczyścić playlistę za pomocą funkcji Player.clearMediaItems()
.
Jeśli odtwarzacz jest wstrzymany, zatrzymany lub nie działa przez ponad 10 minut bez dalszych interakcji użytkownika, usługa zostaje automatycznie przeniesiona ze stanu usługi na pierwszym planie, aby system mógł ją zamknąć. Możesz wdrożyć wznawianie odtwarzania, aby umożliwić użytkownikowi ponowne uruchomienie cyklu życia usługi i wznowienie odtwarzania w późniejszym czasie.
Personalizacja powiadomień
Metadane dotyczące aktualnie odtwarzanego elementu można dostosować, modyfikując MediaItem.MediaMetadata
. Jeśli chcesz zaktualizować metadane dotychczasowego elementu, możesz użyć funkcji Player.replaceMediaItem
, aby zaktualizować metadane bez przerywania odtwarzania.
Możesz też dostosować niektóre przyciski wyświetlane w powiadomieniu, ustawiając niestandardowe ustawienia przycisków multimediów na Androidzie. Więcej informacji o dostosowywaniu ustawień sterowania multimediami w Androidzie
Aby jeszcze bardziej dostosować powiadomienie, utwórz MediaNotification.Provider
za pomocą DefaultMediaNotificationProvider.Builder
lub stwórz niestandardową implementację interfejsu dostawcy. Dodaj dostawcę do MediaSessionService
za pomocą setMediaNotificationProvider
.
Wznowienie odtwarzania
Po zakończeniu MediaSessionService
, a nawet po ponownym uruchomieniu urządzenia, możesz zaoferować wznowienie odtwarzania, aby umożliwić użytkownikom ponowne uruchomienie usługi i wznowienie odtwarzania od miejsca, w którym zostało ono przerwane. Domyślnie wznowienie odtwarzania jest wyłączone. Oznacza to, że użytkownik nie może wznowić odtwarzania, gdy usługa nie jest uruchomiona. Aby włączyć tę funkcję, musisz zadeklarować odbiornik przycisku multimedialnego i zaimplementować metodę onPlaybackResumption
.
Zadeklaruj odbiornik przycisku multimediów Media3
Najpierw zadeklaruj MediaButtonReceiver
w manifeście:
<receiver android:name="androidx.media3.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Wdrażanie wywołania zwrotnego wznowienia odtwarzania
Gdy odtwarzanie zostanie wznowione przez urządzenie Bluetooth lub funkcję wznawiania w interfejsie użytkownika Androida, wywoływana jest metoda wywołania zwrotnego onPlaybackResumption()
.
Kotlin
override fun onPlaybackResumption( mediaSession: MediaSession, controller: ControllerInfo ): ListenableFuture<MediaItemsWithStartPosition> { val settable = SettableFuture.create<MediaItemsWithStartPosition>() scope.launch { // Your app is responsible for storing the playlist and the start position // to use here val resumptionPlaylist = restorePlaylist() settable.set(resumptionPlaylist) } return settable }
Java
@Override public ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption( MediaSession mediaSession, ControllerInfo controller ) { SettableFuture<MediaItemsWithStartPosition> settableFuture = SettableFuture.create(); settableFuture.addListener(() -> { // Your app is responsible for storing the playlist and the start position // to use here MediaItemsWithStartPosition resumptionPlaylist = restorePlaylist(); settableFuture.set(resumptionPlaylist); }, MoreExecutors.directExecutor()); return settableFuture; }
Jeśli masz zapisane inne parametry, takie jak szybkość odtwarzania, tryb powtarzania czy losowy, onPlaybackResumption()
to dobre miejsce na skonfigurowanie odtwarzacza za pomocą tych parametrów, zanim Media3 przygotuje odtwarzacz i rozpocznie odtwarzanie po zakończeniu wywołania zwrotnego.
Zaawansowana konfiguracja kontrolera i wsteczna zgodność
Typowym scenariuszem jest użycie MediaController
w interfejsie aplikacji do sterowania odtwarzaniem i wyświetlania playlisty. Jednocześnie sesja jest dostępna dla klientów zewnętrznych, takich jak elementy sterujące multimediami w Androidzie i Asystent na urządzeniach mobilnych lub telewizorach, Wear OS na zegarkach oraz Android Auto w samochodach. Aplikacja demonstracyjna sesji Media3 to przykład aplikacji, która realizuje taki scenariusz.
Ci zewnętrzni klienci mogą używać interfejsów API, takich jak MediaControllerCompat
starszej biblioteki AndroidX lub android.media.session.MediaController
platformy Android. Media3 jest w pełni zgodny wstecznie z starszą biblioteką i zapewnia interoperacyjność z interfejsem API platformy Android.
Korzystanie z sterownika powiadomień o multimediach
Należy pamiętać, że te starsze i platformowe kontrolery mają ten sam stan i widoczność, których nie można dostosować do konkretnego kontrolera (na przykład PlaybackState.getActions()
i PlaybackState.getCustomActions()
). Możesz użyć kontrolera powiadomień o treściach, aby skonfigurować stan w sesji mediów platformy pod kątem zgodności ze starszymi i platformowymi kontrolerami.
Aplikacja może na przykład udostępnić implementację funkcji MediaSession.Callback.onConnect()
, aby ustawić dostępne polecenia i ustawienia przycisków multimediów w ramach sesji na platformie w ten sposób:
Kotlin
override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { if (session.isMediaNotificationController(controller)) { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build() val playerCommands = ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .build() // Custom button preferences and commands to configure the platform session. return AcceptedResultBuilder(session) .setMediaButtonPreferences( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward)) ) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands with default button preferences for all other controllers. return AcceptedResultBuilder(session).build() }
Java
@Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { if (session.isMediaNotificationController(controller)) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build(); Player.Commands playerCommands = ConnectionResult.DEFAULT_PLAYER_COMMANDS .buildUpon() .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .build(); // Custom button preferences and commands to configure the platform session. return new AcceptedResultBuilder(session) .setMediaButtonPreferences( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward))) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands with default button preferences for all other controllers. return new AcceptedResultBuilder(session).build(); }
Autoryzowanie Androida Auto do wysyłania niestandardowych poleceń
Jeśli używasz sterownika MediaLibraryService
i chcesz obsługiwać Androida Auto w aplikacji mobilnej, sterownik Androida Auto wymaga odpowiednich dostępnych poleceń, w przeciwnym razie Media3 odrzuci przychodzące polecenia niestandardowe z tego sterownika:
Kotlin
override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build() if (session.isMediaNotificationController(controller)) { // [...] See above. } else if (session.isAutoCompanionController(controller)) { // Available session commands to accept incoming custom commands from Auto. return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands for all other controllers. return AcceptedResultBuilder(session).build() }
Java
@Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build(); if (session.isMediaNotificationController(controller)) { // [...] See above. } else if (session.isAutoCompanionController(controller)) { // Available commands to accept incoming custom commands from Auto. return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands for all other controllers. return new AcceptedResultBuilder(session).build(); }
Aplikacja demonstracyjna sesji zawiera moduł motoryzacyjny, który demonstruje obsługę systemu operacyjnego Automotive, który wymaga osobnego pliku APK.