Einfache Mediaplayer-App mit Media3 ExoPlayer erstellen

Jetpack Media3 definiert eine Player-Schnittstelle, die grundlegende Funktionen für die Wiedergabe von Video- und Audiodateien beschreibt. ExoPlayer ist die Standardimplementierung dieser Schnittstelle in Media3. Wir empfehlen die Verwendung von ExoPlayer, da es eine umfassende Reihe von Funktionen bietet, die die meisten Anwendungsfälle für die Wiedergabe abdecken, und das sich auf zusätzliche Anwendungsfälle anpassen lässt. ExoPlayer entfernt außerdem Geräte- und Betriebssystemfragmentierung, damit Ihr Code konsistent im gesamten Android-Ökosystem funktioniert. ExoPlayer umfasst:

Auf dieser Seite werden Sie durch einige der wichtigsten Schritte beim Erstellen einer Wiedergabe-App geführt. Weitere Informationen finden Sie in unseren vollständigen Leitfäden zu Media3 ExoPlayer.

Erste Schritte

Fügen Sie zunächst eine Abhängigkeit vom ExoPlayer, der Benutzeroberfläche und den allgemeinen Modulen von Jetpack Media3 hinzu:

implementation "androidx.media3:media3-exoplayer:1.3.1"
implementation "androidx.media3:media3-ui:1.3.1"
implementation "androidx.media3:media3-common:1.3.1"

Je nach Anwendungsfall benötigst du möglicherweise auch zusätzliche Module von Media3, z. B. exoplayer-dash, um Streams im DASH-Format wiederzugeben.

Ersetzen Sie 1.3.1 durch Ihre bevorzugte Version der Bibliothek. Die neueste Version finden Sie in den Versionshinweisen.

Mediaplayer erstellen

Bei Media3 können Sie entweder die enthaltene Implementierung der Player-Schnittstelle (ExoPlayer) verwenden oder Ihre eigene benutzerdefinierte Implementierung erstellen.

Erstellen eines ExoPlayers

Die einfachste Methode zum Erstellen einer ExoPlayer-Instanz ist folgende:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

Sie können Ihren Mediaplayer in der Lebenszyklusmethode onCreate() des Speicherorts Activity, Fragment oder Service erstellen.

Das Builder bietet eine Reihe von Anpassungsoptionen, die für Sie interessant sein könnten, z. B.:

Media3 bietet eine PlayerView-UI-Komponente, die du in die Layoutdatei deiner App aufnehmen kannst. Diese Komponente enthält eine PlayerControlView für Wiedergabesteuerung, eine SubtitleView zum Anzeigen von Untertiteln und eine Surface für das Rendern von Videos.

Player wird vorbereitet

Füge einer Playlist Medienelemente für die Wiedergabe mit Methoden wie setMediaItem() und addMediaItem() hinzu. Rufen Sie dann prepare() auf, um mit dem Laden von Medien zu beginnen und die erforderlichen Ressourcen abzurufen.

Sie sollten diese Schritte nicht ausführen, bevor die App im Vordergrund ausgeführt wird. Wenn sich Ihr Player in einem Activity- oder Fragment-Element befindet, bedeutet dies, dass er in der Lebenszyklusmethode onStart() auf API-Level 24 und höher oder in der Lebenszyklusmethode onResume() auf API-Ebene 23 und niedriger vorbereitet wird. Für einen Spieler in einem Service können Sie ihn in onCreate() vorbereiten.

Player steuern

Nachdem der Player vorbereitet wurde, können Sie die Wiedergabe steuern, indem Sie Methoden wie die folgenden aufrufen:

UI-Komponenten wie PlayerView oder PlayerControlView werden bei der Verknüpfung mit einem Spieler entsprechend aktualisiert.

Player loslassen

Für die Wiedergabe sind möglicherweise Ressourcen erforderlich, die nur begrenzt verfügbar sind, z. B. Video-Decoder. Daher ist es wichtig, release() im Player aufzurufen, um Ressourcen freizugeben, wenn der Player nicht mehr benötigt wird.

Wenn sich Ihr Player in einem Activity- oder Fragment-Element befindet, geben Sie ihn über die Lebenszyklusmethode onStop() auf API-Level 24 und höher oder die Methode onPause() auf API-Ebene 23 und niedriger frei. Spieler, die sich in einem Service befinden, können in onDestroy() freigegeben werden.

Wiedergabe mit einer Mediensitzung verwalten

Unter Android bieten Mediensitzungen eine standardisierte Möglichkeit, mit einem Medienplayer über Prozessgrenzen hinweg zu interagieren. Wenn Sie eine Mediensitzung mit Ihrem Player verbinden, können Sie die Medienwiedergabe extern bewerben und Wiedergabebefehle von externen Quellen empfangen, z. B. zur Einbindung in Systemmediensteuerelemente auf Mobilgeräten und Geräten mit großen Bildschirmen.

Fügen Sie eine Abhängigkeit vom Modul „Media3 Session“ hinzu, um Mediensitzungen zu verwenden:

implementation "androidx.media3:media3-session:1.3.1"

Mediensitzung erstellen

So kannst du ein MediaSession nach der Initialisierung eines Players erstellen:

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();

Media3 synchronisiert den Status von Player automatisch mit dem Status von MediaSession. Dies funktioniert mit jeder Player-Implementierung, einschließlich ExoPlayer, CastPlayer oder einer benutzerdefinierten Implementierung.

Anderen Clients Kontrolle gewähren

Client-Apps können einen Mediencontroller implementieren, um die Wiedergabe Ihrer Mediensitzung zu steuern. Damit du diese Anfragen erhalten kannst, musst du beim Erstellen des MediaSession ein Callback-Objekt festlegen.

Wenn ein Controller eine Verbindung zu Ihrer Mediensitzung herstellt, wird die Methode onConnect() aufgerufen. Mit dem angegebenen ControllerInfo können Sie entscheiden, ob Sie die Anfrage annehmen oder ablehnen möchten. Ein Beispiel hierzu finden Sie in der Demo-App „Media3 Session“.

Sobald die Verbindung hergestellt ist, kann der Controller Wiedergabebefehle an die Sitzung senden. Die Sitzung delegiert diese Befehle dann an den Spieler. Die auf der Player-Oberfläche definierten Wiedergabe- und Playlist-Befehle werden von der Sitzung automatisch ausgeführt.

Mit anderen Callback-Methoden können Sie z. B. Anfragen für benutzerdefinierte Wiedergabebefehle verarbeiten und die Playlist ändern. Diese Callbacks enthalten ebenfalls ein ControllerInfo-Objekt, sodass Sie die Zugriffssteuerung für einzelne Anfragen festlegen können.

Medien im Hintergrund abspielen

Wenn Sie Medien auch dann abspielen möchten, wenn Ihre App nicht im Vordergrund ausgeführt wird und z. B. Musik, Hörbücher oder Podcasts abgespielt werden sollen, auch wenn der Nutzer die App nicht geöffnet hat, müssen Player und MediaSession in einem Dienst im Vordergrund gekapselt werden. Media3 stellt zu diesem Zweck die Schnittstelle MediaSessionService bereit.

MediaSessionService implementieren

Erstellen Sie eine Klasse, die MediaSessionService erweitert, und instanziieren Sie MediaSession in der Lebenszyklusmethode onCreate().

Kotlin

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession 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;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

Fügen Sie in Ihrem Manifest die Klasse Service mit einem MediaSessionService-Intent-Filter hinzu und fordern Sie die Berechtigung FOREGROUND_SERVICE an, um einen Dienst im Vordergrund auszuführen:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Überschreiben Sie schließlich in der von Ihnen erstellten Klasse die Methode onGetSession(), um den Clientzugriff auf Ihre Mediensitzung zu steuern. Geben Sie MediaSession zurück, um die Verbindungsanfrage anzunehmen, oder null, um die Anfrage abzulehnen.

Kotlin

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Java

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

Verbindung zur UI herstellen

Da sich Ihre Mediensitzung nun in einem Service befindet, das von der Activity oder Fragment getrennt ist, auf der sich die Benutzeroberfläche Ihres Players befindet, können Sie sie mithilfe eines MediaController miteinander verknüpfen. Erstellen Sie in der Methode onStart() von Activity oder von Fragment mit Ihrer UI einen SessionToken für Ihre MediaSession und verwenden Sie dann SessionToken, um einen MediaController zu erstellen. Das Erstellen eines MediaController erfolgt asynchron.

Kotlin

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Java

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController implementiert die Player-Schnittstelle, sodass Sie die gleichen Methoden wie play() und pause() zur Steuerung der Wiedergabe verwenden können. Ähnlich wie bei anderen Komponenten sollten Sie die MediaController freigeben, wenn sie nicht mehr benötigt wird (z. B. die Lebenszyklusmethode onStop() eines Activity), indem Sie MediaController.releaseFuture() aufrufen.

Benachrichtigung veröffentlichen

Dienste im Vordergrund sind erforderlich, um eine Benachrichtigung zu veröffentlichen, während sie aktiv sind. Eine MediaSessionService erstellt automatisch eine MediaStyle-Benachrichtigung in Form einer MediaNotification. Wenn du eine benutzerdefinierte Benachrichtigung bereitstellen möchtest, musst du einen MediaNotification.Provider mit DefaultMediaNotificationProvider.Builder erstellen oder eine benutzerdefinierte Implementierung der Anbieterschnittstelle erstellen. Fügen Sie Ihren Anbieter mit setMediaNotificationProvider zu MediaSession hinzu.

Werbung für die Inhaltsbibliothek

Ein MediaLibraryService baut auf einem MediaSessionService auf, da es Clientanwendungen ermöglicht, die von Ihrer Anwendung bereitgestellten Medieninhalte zu durchsuchen. Client-Apps implementieren einen MediaBrowser, um mit Ihrer MediaLibraryService zu interagieren.

Die Implementierung von MediaLibraryService ähnelt der Implementierung von MediaSessionService, mit der Ausnahme, dass du in onGetSession() anstelle von MediaSession ein MediaLibrarySession zurückgeben solltest. Im Vergleich zu MediaSession.Callback enthält MediaLibrarySession.Callback zusätzliche Methoden, mit denen ein Browserclient die von deinem Bibliotheksdienst angebotenen Inhalte aufrufen kann.

Deklarieren Sie ähnlich wie bei MediaSessionService den MediaLibraryService in Ihrem Manifest und fordern Sie die Berechtigung FOREGROUND_SERVICE an, um einen Dienst im Vordergrund auszuführen:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Das obige Beispiel enthält einen Intent-Filter sowohl für MediaLibraryService als auch für die Abwärtskompatibilität für den alten MediaBrowserService. Mit dem zusätzlichen Intent-Filter können Client-Apps, die die MediaBrowserCompat API verwenden, Ihr Service erkennen.

Mit einem MediaLibrarySession können Sie Ihre Inhaltsbibliothek in einer Baumstruktur mit einem einzigen Stamm-MediaItem bereitstellen. Jeder MediaItem in der Baumstruktur kann eine beliebige Anzahl von untergeordneten MediaItem-Knoten haben. Sie können je nach Anfrage der Clientanwendung einen anderen Stamm oder einen anderen Baum bereitstellen. Beispiel: Der Baum, den Sie an einen Client zurückgeben, der nach einer Liste mit empfohlenen Medienelementen sucht, kann nur den Stamm-MediaItem und eine einzelne Ebene von untergeordneten MediaItem-Knoten enthalten, während die Struktur, die Sie zu einer anderen Client-App zurückkehren, eine umfassendere Inhaltsbibliothek darstellen.

MediaLibrarySession wird erstellt

Ein MediaLibrarySession erweitert die MediaSession API um Content Browsing APIs. Im Vergleich zum MediaSession-Callback werden beim MediaLibrarySession-Callback z. B. folgende Methoden hinzugefügt:

  • onGetLibraryRoot(), wenn ein Client den Stamm MediaItem eines Inhaltsbaums anfordert
  • onGetChildren(), wenn ein Client die untergeordneten Elemente von MediaItem im Inhaltsbaum anfordert
  • onGetSearchResult(), wenn ein Client Suchergebnisse aus dem Inhaltsbaum für eine bestimmte Abfrage anfordert

Relevante Callback-Methoden umfassen ein LibraryParams-Objekt mit zusätzlichen Signalen zur Art der Inhaltsstruktur, an der eine Client-App interessiert ist.