Sesje multimedialne to uniwersalny sposób na interakcję z odtwarzaczem audio lub wideo. W Media3 domyślnym odtwarzaczem jest klasa ExoPlayer
, która implementuje interfejs Player
. Połączenie sesji multimediów z odtwarzaczem umożliwia aplikacji reklamowanie odtwarzania multimediów na zewnątrz i odbieranie poleceń odtwarzania ze źródeł zewnętrznych.
Polecenia mogą być wykonywane z przycisków fizycznych, takich jak przycisk odtwarzania na słuchawkach czy pilota telewizora. Mogą też pochodzić z aplikacji klienckich mających kontroler multimediów, np. instruujących Asystenta Google „wstrzymanie”. Sesja multimediów przekazuje te polecenia do odtwarzacza aplikacji do multimediów.
Kiedy wybrać sesję multimediów
Gdy wdrożysz MediaSession
, umożliwisz użytkownikom sterowanie odtwarzaniem:
- Przez słuchawki. Często są to przyciski lub interakcje dotykowe, które użytkownik może wykonać na słuchawkach, aby włączyć lub wstrzymać odtwarzanie multimediów albo przejść do następnego lub poprzedniego utworu.
- Rozmawiając z Asystentem Google. Często używany schemat jest mówienie „OK Google, wstrzymaj”, by wstrzymać odtwarzanie multimediów.
- Na zegarku z Wear OS. Ułatwia to dostęp do najczęściej używanych elementów sterujących odtwarzaniem podczas gry na telefonie.
- Za pomocą opcji sterowania multimediami. Ta karuzela pokazuje elementy sterujące każdej uruchomionej sesji multimedialnej.
- Na telewizorze. Umożliwia wykonywanie działań związanych z fizycznymi przyciskami odtwarzania, sterowaniem odtwarzaniem na platformie i zarządzaniem zasilaniem.
- oraz wszelkie inne procesy zewnętrzne, które muszą wpłynąć na odtwarzanie.
Taka konfiguracja sprawdza się w wielu zastosowaniach. Używanie właściwości MediaSession
zalecamy szczególnie wtedy, gdy:
- Prowadzisz długie treści wideo, np. filmy lub telewizję na żywo.
- Odtwarzasz długie treści audio, takie jak podcasty lub playlisty muzyczne.
- Tworzysz aplikację telewizyjną.
Jednak nie wszystkie przypadki użycia dobrze pasują do MediaSession
. Funkcji Player
możesz używać tylko w tych przypadkach:
- Wyświetlasz krótkie treści, w których kluczowe znaczenie ma interakcja i zaangażowanie użytkowników.
- Nie ma żadnego aktywnego filmu, tak jak by użytkownik przewijał listę i na ekranie wyświetlało się kilka filmów jednocześnie.
- Odtwarzam jednorazowy film z wprowadzeniem lub wyjaśnieniem, który powinien aktywnie oglądać użytkownik.
- Twoje treści mają ochronę prywatności i nie chcesz, aby zewnętrzne procesy miały dostęp do metadanych multimediów (np. tryb incognito w przeglądarce).
Jeśli Twój przypadek użycia nie pasuje do żadnej z powyższych sytuacji, zastanów się, czy możesz nadal odtwarzać aplikację, gdy użytkownik nie wchodzi w interakcję z treściami. Jeśli odpowiedź jest twierdząca, prawdopodobnie wybierz MediaSession
. Jeśli odpowiedź brzmi „nie”, prawdopodobnie lepiej będzie użyć funkcji Player
.
Tworzenie sesji multimediów
Sesja multimedialna działa razem z odtwarzaczem, którym zarządza. Sesję multimediów można utworzyć za pomocą obiektu Context
i Player
. Sesję multimediów należy utworzyć i zainicjować w razie potrzeby, np. stosując metodę cyklu życia onStart()
lub onResume()
funkcji Activity
lub Fragment
albo metodę onCreate()
zasobu Service
, do którego należy sesja multimediów i powiązany z nią odtwarzacz.
Aby utworzyć sesję multimediów, zainicjuj obiekt Player
i przekaż go do MediaSession.Builder
w ten sposób:
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Automatyczna obsługa stanów
Biblioteka Media3 automatycznie aktualizuje sesję multimediów na podstawie stanu odtwarzacza. W związku z tym nie musisz ręcznie obsługiwać mapowania z odtwarzacza na sesję.
To przerwa od starszego podejścia, w ramach którego trzeba było utworzyć i utrzymywać element PlaybackStateCompat
niezależnie od samego odtwarzacza, na przykład w celu wykrycia błędów.
Unikalny identyfikator sesji
Domyślnie MediaSession.Builder
tworzy sesję z pustym ciągiem znaków jako jej identyfikator. Jest to wystarczające, jeśli aplikacja zamierza utworzyć tylko jedną instancję sesji, co jest najczęściej spotykanym przypadkiem.
Jeśli aplikacja chce zarządzać wieloma wystąpieniami sesji jednocześnie, musi zadbać o to, by identyfikator każdej sesji był niepowtarzalny. Identyfikator sesji można ustawić podczas tworzenia sesji za pomocą funkcji MediaSession.Builder.setId(String id)
.
Jeśli zobaczysz, że IllegalStateException
powoduje awarię aplikacji i wyświetla się komunikat o błędzie IllegalStateException: Session ID must be unique. ID=
, prawdopodobnie sesja została nieoczekiwanie utworzona przed udostępnieniem wcześniej utworzonej instancji o tym samym identyfikatorze. Aby uniknąć wycieku sesji przez błąd programowania, takie przypadki są wykrywane i powiadamiane jako wyjątek.
Przyznaj kontrolę innym klientom
Sesja multimediów to klucz do sterowania odtwarzaniem. Pozwala kierować polecenia ze źródeł zewnętrznych do odtwarzacza, w którym odtwarzasz multimedia. Mogą to być przyciski fizyczne, takie jak przycisk odtwarzania na słuchawkach lub pilocie telewizora, albo polecenia pośrednie, np. polecenie „wstrzymaj” Asystentowi Google. Możesz też przyznać dostęp do systemu Android, który ułatwia sterowanie powiadomieniami i ekranem blokady, lub do zegarka z Wear OS, który umożliwia sterowanie odtwarzaniem z poziomu tarczy zegarka. Klienty zewnętrzne mogą używać kontrolera multimediów do wydawania poleceń dotyczących odtwarzania w Twojej aplikacji do multimediów. Są one odbierane przez sesję multimediów, która ostatecznie przekazuje te polecenia do odtwarzacza.
Gdy kontroler ma zamiar połączyć się z sesją multimediów, wywoływana jest metoda onConnect()
. Możesz użyć podanego ControllerInfo
, aby zdecydować, czy chcesz zaakceptować, czy odrzucić prośbę. Przykład akceptowania żądania połączenia znajdziesz w sekcji Zadeklaruj dostępne polecenia.
Po połączeniu kontroler może wysyłać do sesji polecenia odtwarzania. Następnie sesja przekazuje te polecenia do odtwarzacza. Polecenia dotyczące odtwarzania i playlist zdefiniowane w interfejsie Player
są automatycznie obsługiwane przez sesję.
Inne metody wywołania zwrotnego umożliwiają na przykład obsługę żądań niestandardowych poleceń odtwarzania czy modyfikowania playlisty).
Te wywołania zwrotne również zawierają obiekt ControllerInfo
, dzięki czemu możesz zmienić sposób odpowiadania na poszczególne żądania z uwzględnieniem każdego kontrolera.
Modyfikowanie playlisty
Sesja multimediów może bezpośrednio modyfikować playlistę w odtwarzaczu, zgodnie z opisem w przewodniku ExoPlayer dotyczącym playlist.
Kontrolery również mogą modyfikować playlistę, jeśli
COMMAND_SET_MEDIA_ITEM
lub COMMAND_CHANGE_MEDIA_ITEMS
są dostępne dla kontrolera.
Gdy dodajesz nowe elementy do playlisty, odtwarzacz zazwyczaj wymaga instancji MediaItem
ze zdefiniowanym identyfikatorem URI, aby umożliwić odtwarzanie. Domyślnie nowo dodane elementy są automatycznie przekazywane do metod odtwarzacza, takich jak player.addMediaItem
, jeśli mają zdefiniowany identyfikator URI.
Jeśli chcesz dostosować instancje MediaItem
dodane do odtwarzacza, możesz zastąpić
onAddMediaItems()
.
Ten krok jest potrzebny, gdy chcesz obsługiwać kontrolery, które żądają multimediów bez zdefiniowanego identyfikatora URI. Zamiast tego MediaItem
zwykle ma ustawione co najmniej 1 z tych pól do opisywania żądanych multimediów:
MediaItem.id
: ogólny identyfikator identyfikujący multimedia.MediaItem.RequestMetadata.mediaUri
: identyfikator URI żądania, który może korzystać ze schematu niestandardowego i nie musi być bezpośrednio odtwarzany przez odtwarzacz.MediaItem.RequestMetadata.searchQuery
: zapytanie wpisane w formie tekstowej, np. z Asystenta Google.MediaItem.MediaMetadata
: uporządkowane metadane, np. „tytuł” lub „wykonawca”.
Aby uzyskać więcej opcji dostosowywania do zupełnie nowych playlist, możesz też zastąpić parametr onSetMediaItems()
, który pozwala określić pozycję początkową i pozycję playlisty. Możesz na przykład rozwinąć żądany element do całej playlisty i poinstruować odtwarzacz, aby rozpoczynał od indeksu pierwotnie żądanego elementu. Przykładową implementację metody onSetMediaItems()
z tą funkcją znajdziesz w aplikacji demonstracyjnej sesji.
Zarządzanie układem niestandardowym i poleceniami niestandardowymi
W poniższych sekcjach opisano, jak reklamować niestandardowy układ niestandardowych przycisków poleceń w aplikacjach klienckich i autoryzować kontrolery do wysyłania niestandardowych poleceń.
Zdefiniuj niestandardowy układ sesji
Aby wskazać aplikacjom klienckim, które elementy sterujące odtwarzaniem chcesz udostępnić użytkownikowi, ustaw niestandardowy układ sesji podczas tworzenia obiektu MediaSession
w metodzie onCreate()
usługi.
Kotlin
override fun onCreate() { super.onCreate() val likeButton = CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle())) .build() session = MediaSession.Builder(this, player) .setCallback(CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build() }
Java
@Override public void onCreate() { super.onCreate(); CommandButton likeButton = new CommandButton.Builder() .setDisplayName("Like") .setIconResId(R.drawable.like_icon) .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)) .build(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); Player player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player) .setCallback(new CustomMediaSessionCallback()) .setCustomLayout(ImmutableList.of(likeButton, favoriteButton)) .build(); }
Zadeklaruj dostępny odtwarzacz i polecenia niestandardowe
Aplikacje multimedialne mogą definiować polecenia niestandardowe, których można używać na przykład w układzie niestandardowym. Można na przykład wdrożyć przyciski, które pozwolą użytkownikowi zapisać element multimedialny na liście ulubionych. MediaController
wysyła polecenia niestandardowe, a MediaSession.Callback
je otrzymuje.
Możesz określić, które niestandardowe polecenia sesji będą dostępne dla MediaController
, gdy połączy się on z sesją multimediów. Jest to możliwe, zastępując ustawienie MediaSession.Callback.onConnect()
. Skonfiguruj i zwróć zestaw dostępnych poleceń podczas akceptowania żądania połączenia z MediaController
w metodzie wywołania zwrotnego onConnect
:
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { // Configure commands available to the controller in onConnect() override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY)) .build() return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { // Configure commands available to the controller in onConnect() @Override public ConnectionResult onConnect( MediaSession session, ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle())) .build(); return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } }
Aby otrzymywać niestandardowe polecenia z MediaController
, zastąp metodę onCustomCommand()
w tabeli Callback
.
Kotlin
private inner class CustomMediaSessionCallback: MediaSession.Callback { ... override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == SAVE_TO_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture( SessionResult(SessionResult.RESULT_SUCCESS) ) } ... } }
Java
class CustomMediaSessionCallback implements MediaSession.Callback { ... @Override public ListenableFuture<SessionResult> onCustomCommand( MediaSession session, ControllerInfo controller, SessionCommand customCommand, Bundle args ) { if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture( new SessionResult(SessionResult.RESULT_SUCCESS) ); } ... } }
Aby prześledzić, który kontroler mediów wysyła żądanie, użyj właściwości packageName
obiektu MediaSession.ControllerInfo
przekazywanej do metod Callback
. Dzięki temu możesz dostosować działanie aplikacji w odpowiedzi na dane polecenie, jeśli pochodzi ono z systemu, Twojej aplikacji lub innych aplikacji klienckich.
Aktualizacja układu niestandardowego po interakcji użytkownika
Po wykonaniu niestandardowego polecenia lub innej interakcji z odtwarzaczem możesz zaktualizować układ wyświetlany w interfejsie kontrolera. Typowym przykładem jest przycisk przełączania, który zmienia swoją ikonę po uruchomieniu powiązanego z nim działania. Aby zaktualizować układ, możesz użyć MediaSession.setCustomLayout
:
Kotlin
val removeFromFavoritesButton = CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle())) .build() mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))
Java
CommandButton removeFromFavoritesButton = new CommandButton.Builder() .setDisplayName("Remove from favorites") .setIconResId(R.drawable.favorite_remove_icon) .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle())) .build(); mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));
Dostosowywanie działania poleceń odtwarzania
Aby dostosować działanie polecenia zdefiniowanego w interfejsie Player
, np. play()
lub seekToNext()
, umieść Player
w ForwardingPlayer
.
Kotlin
val player = ExoPlayer.Builder(context).build() val forwardingPlayer = object : ForwardingPlayer(player) { override fun play() { // Add custom logic super.play() } override fun setPlayWhenReady(playWhenReady: Boolean) { // Add custom logic super.setPlayWhenReady(playWhenReady) } } val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) { @Override public void play() { // Add custom logic super.play(); } @Override public void setPlayWhenReady(boolean playWhenReady) { // Add custom logic super.setPlayWhenReady(playWhenReady); } }; MediaSession mediaSession = new MediaSession.Builder(context, forwardingPlayer).build();
Więcej informacji o ForwardingPlayer
znajdziesz w przewodniku ExoPlayer dotyczącym dostosowywania.
Identyfikowanie kontrolera żądającego polecenia odtwarzacza
Jeśli wywołanie metody Player
pochodzi z metody MediaController
, możesz zidentyfikować źródło pochodzenia za pomocą MediaSession.controllerForCurrentRequest
i uzyskać ControllerInfo
dla bieżącego żądania:
Kotlin
class CallerAwareForwardingPlayer(player: Player) : ForwardingPlayer(player) { override fun seekToNext() { Log.d( "caller", "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}" ) super.seekToNext() } }
Java
public class CallerAwareForwardingPlayer extends ForwardingPlayer { public CallerAwareForwardingPlayer(Player player) { super(player); } @Override public void seekToNext() { Log.d( "caller", "seekToNext called from package: " + session.getControllerForCurrentRequest().getPackageName()); super.seekToNext(); } }
Reagowanie na przyciski multimediów
Przyciski multimediów to przyciski na urządzeniach z Androidem i innych urządzeniach peryferyjnych, np. przycisk odtwarzania/wstrzymywania na słuchawkach Bluetooth. Media3 obsługuje za Ciebie zdarzenia przycisku multimediów po dotarciu do sesji i wywołuje odpowiednią metodę Player
w odtwarzaczu sesji.
Aplikacja może zastąpić domyślne zachowanie, zastępując ustawienie MediaSession.Callback.onMediaButtonEvent(Intent)
. W takim przypadku
aplikacja może samodzielnie obsługiwać wszystkie specyfikacje interfejsu API.