Często dobrze jest odtwarzać multimedia, gdy aplikacja nie działa na pierwszym planie. Na przykład odtwarzacz muzyki zwykle odtwarza muzykę wtedy, gdy użytkownik zablokował urządzenie lub korzysta z innej aplikacji. Biblioteka Media3 udostępnia szereg interfejsów, które umożliwiają odtwarzanie w tle.
Korzystanie z usługi MediaSessionService
Aby włączyć odtwarzanie w tle, umieść Player
i MediaSession
w osobnej usłudze.
Dzięki temu urządzenie może wyświetlać multimedia nawet wtedy, gdy aplikacja nie działa na pierwszym planie.
![Usługa MediaSessionService umożliwia uruchamianie sesji multimediów niezależnie od aktywności aplikacji](https://developer.android.google.cn/static/guide/topics/media/images/mediasessionservice.png?authuser=0000&hl=pl)
MediaSessionService
umożliwia uruchamianie sesji multimediów niezależnie od aktywności aplikacjiJeśli hostujesz odtwarzacz w usłudze, używaj MediaSessionService
.
Aby to zrobić, utwórz klasę, która rozszerza zakres MediaSessionService
, a następnie utwórz w niej sesję multimediów.
Korzystanie z MediaSessionService
pozwala klientom zewnętrznym, takim jak Asystent Google, elementy sterujące multimediami w systemie czy urządzenia towarzyszące, takie jak Wear OS, mogą wykrywać Twoją usługę, łączyć się z nią i sterować odtwarzaniem bez dostępu do Twojej aktywności w interfejsie aplikacji. Z jednym MediaSessionService
może być połączonych jednocześnie wiele aplikacji klienckich, z których każda ma własną MediaController
.
Wdrażanie cyklu życia usługi
Musisz wdrożyć 3 metody cyklu życia usługi:
- Funkcja
onCreate()
jest wywoływana, gdy ma się połączyć pierwszy kontroler, a usługa jest inicjowana i uruchamiana. To najlepsze miejsce do tworzeniaPlayer
iMediaSession
. - Pole
onTaskRemoved(Intent)
jest wywoływane, gdy użytkownik zamknie aplikację z ostatnich zadań. Jeśli odtwarzanie jest w toku, aplikacja może pozostawić usługę na pierwszym planie. Jeśli odtwarzacz jest wstrzymany, oznacza to, że usługa nie jest na pierwszym planie i należy ją zatrzymać. - Funkcja
onDestroy()
jest wywoływana podczas zatrzymywania usługi. Wszystkie zasoby, w tym odtwarzacz i sesja, muszą zostać zwolnione.
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() } // The user dismissed the app from the recent tasks override fun onTaskRemoved(rootIntent: Intent?) { val player = mediaSession?.player!! if (!player.playWhenReady || player.mediaItemCount == 0 || player.playbackState == Player.STATE_ENDED) { // Stop the service if not playing, continue playing in the background // otherwise. stopSelf() } } // 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(); } // The user dismissed the app from the recent tasks @Override public void onTaskRemoved(@Nullable Intent rootIntent) { Player player = mediaSession.getPlayer(); if (!player.getPlayWhenReady() || player.getMediaItemCount() == 0 || player.getPlaybackState() == Player.STATE_ENDED) { // Stop the service if not playing, continue playing in the background // otherwise. stopSelf(); } } // Remember to release the player and media session in onDestroy @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
Zamiast kontynuować odtwarzanie w tle, aplikacja może zatrzymać usługę w każdym przypadku, gdy użytkownik zamknie aplikację:
Kotlin
override fun onTaskRemoved(rootIntent: Intent?) { val player = mediaSession.player if (player.playWhenReady) { // Make sure the service is not in foreground. player.pause() } stopSelf() }
Java
@Override public void onTaskRemoved(@Nullable Intent rootIntent) { Player player = mediaSession.getPlayer(); if (player.getPlayWhenReady()) { // Make sure the service is not in foreground. player.pause(); } stopSelf(); }
Zapewnianie dostępu do sesji multimediów
Zastąp metodę onGetSession()
, aby przyznać innym klientom dostęp do sesji multimedialnej 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ń do uruchomienia usługi na pierwszym planie. Dodaj do pliku manifestu uprawnienia FOREGROUND_SERVICE
, a jeśli kierujesz reklamy na interfejs API w wersji 34 lub nowszej – także FOREGROUND_SERVICE_MEDIA_PLAYBACK
:
<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
.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
Musisz zdefiniować obiekt foregroundServiceType
obejmujący mediaPlayback
, gdy aplikacja działa na urządzeniu z Androidem 10 (poziom interfejsu API 29) lub nowszym.
Steruj odtwarzaniem za pomocą: MediaController
W aktywności lub fragmencie zawierającym interfejs odtwarzacza możesz ustanowić połączenie między interfejsem a sesją multimediów za pomocą MediaController
. Kontroler multimediów wysyła z niego polecenia do odtwarzacza w sesji. Szczegółowe informacje o tworzeniu i używaniu MediaController
znajdziesz w przewodniku Tworzenie MediaController
.
Obsługa poleceń interfejsu
MediaSession
odbiera polecenia z kontrolera za pomocą modułu MediaSession.Callback
. Inicjowanie MediaSession
powoduje utworzenie domyślnej implementacji MediaSession.Callback
, która automatycznie obsługuje wszystkie polecenia wysyłane przez MediaController
do odtwarzacza.
Powiadomienie
MediaSessionService
automatycznie tworzy za Ciebie MediaNotification
, które w większości przypadków powinno działać. Domyślnie opublikowane powiadomienie jest powiadomieniem MediaStyle
, w którym zawiera najświeższe informacje z sesji multimediów i zawiera elementy sterujące odtwarzaniem. Urządzenie MediaNotification
wie o Twojej sesji i może sterować odtwarzaniem innych aplikacji połączonych z tą samą sesją.
Na przykład aplikacja do strumieniowego odtwarzania muzyki, która korzysta z MediaSessionService
, mogłaby utworzyć element MediaNotification
zawierający tytuł, wykonawcę i okładkę albumu bieżącego elementu multimedialnego odtwarzanego razem z elementami sterującymi odtwarzaniem zgodnie z konfiguracją MediaSession
.
Wymagane metadane można podać w mediach lub zadeklarować je jako część elementu multimedialnego, jak w tym fragmencie:
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();
Aplikacje mogą dostosować przyciski poleceń w elementach sterujących Android Media. Więcej informacji o dostosowywaniu elementów sterujących Android Media
Dostosowywanie powiadomień
Aby dostosować powiadomienie, utwórz MediaNotification.Provider
z DefaultMediaNotificationProvider.Builder
lub niestandardową implementację interfejsu dostawcy. Dodaj dostawcę do usługi MediaSessionService
za pomocą aplikacji setMediaNotificationProvider
.
Wznowienie odtwarzania
Przyciski multimediów to przyciski sprzętowe na urządzeniach z Androidem i innych urządzeniach peryferyjnych, np. przycisk odtwarzania lub wstrzymywania na słuchawkach Bluetooth. Media3 obsługuje za Ciebie wejściowe przyciski multimediów, gdy usługa jest uruchomiona.
Zadeklarowanie odbiornika przycisku multimediów Media3
Media3 udostępnia interfejs API, który umożliwia użytkownikom wznawianie odtwarzania po zamknięciu aplikacji, a nawet po ponownym uruchomieniu urządzenia. Domyślnie wznawianie odtwarzania jest wyłączone. Oznacza to, że użytkownik nie może wznowić odtwarzania, gdy usługa nie jest uruchomiona. Aby to zrobić, najpierw zadeklaruj element MediaButtonReceiver
w pliku manifestu:
<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 urządzenie Bluetooth lub funkcja wznowienia w interfejsie Androida zgłasza żądanie wznowienia odtwarzania, 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 tryb losowy, onPlaybackResumption()
jest dobrym miejscem do skonfigurowania odtwarzacza z tymi parametrami, zanim Media3 przygotuje go i rozpocznie odtwarzanie po zakończeniu wywołania zwrotnego.
Zaawansowana konfiguracja kontrolera i zgodność wsteczna
Typowym scenariuszem jest używanie w interfejsie aplikacji MediaController
do sterowania odtwarzaniem i wyświetlaniem playlisty. W tym samym czasie sesja jest dostępna dla klientów zewnętrznych, takich jak opcje sterowania multimediami na Androidzie i Asystent na urządzeniach mobilnych lub telewizorach,
Wear OS na zegarki i Android Auto w samochodach. Przykładem aplikacji, w której można zastosować taki scenariusz, jest aplikacja demonstracyjna sesji Media3.
Te klienty zewnętrzne mogą używać interfejsów API, takich jak MediaControllerCompat
starszej biblioteki AndroidaX lub android.media.session.MediaController
platformy Android. Media3 jest w pełni zgodna wstecznie ze starszą biblioteką i zapewnia interoperacyjność z interfejsem Android Framework API.
Użyj kontrolera powiadomień o multimediach
Pamiętaj, że starsze kontrolery lub kontrolery platformy odczytują te same wartości z platformy PlaybackState.getActions()
i PlaybackState.getCustomActions()
. Aby określić działania i działania niestandardowe w ramach sesji platformy, aplikacja może użyć kontrolera powiadomień o multimediach i ustawić dostępne polecenia oraz układ niestandardowy. Usługa łączy kontroler powiadomień o multimediach z sesją, a sesja używa obiektu ConnectionResult
zwróconego przez onConnect()
wywołania zwrotnego do konfigurowania działań i niestandardowych działań sesji platformy.
Biorąc pod uwagę sytuację tylko na urządzeniach mobilnych, aplikacja może udostępnić implementację MediaSession.Callback.onConnect()
do ustawiania dostępnych poleceń i układu niestandardowego na potrzeby sesji platformy 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 layout and available commands to configure the legacy/framework session. return AcceptedResultBuilder(session) .setCustomLayout( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward)) ) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands with default custom layout 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 layout and available commands to configure the legacy/framework session. return new AcceptedResultBuilder(session) .setCustomLayout( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward))) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands without default custom layout for all other controllers. return new AcceptedResultBuilder(session).build(); }
Autoryzowanie Androida Auto do wysyłania poleceń niestandardowych
Gdy używasz MediaLibraryService
i Android Auto działa z aplikacją mobilną, kontroler Androida Auto wymaga odpowiednich dostępnych poleceń. W przeciwnym razie Media3 odrzuca przychodzące polecenia niestandardowe z tego kontrolera:
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 with default custom layout 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 without default custom layout 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.