Einfache Mediaplayer-App mit Media3 ExoPlayer erstellen

Jetpack Media3 definiert eine Player-Schnittstelle, die grundlegende Funktionen für die Wiedergabe von Video- und Audiodateien bietet. ExoPlayer ist die Standardimplementierung dieser Schnittstelle in Media3. Wir empfehlen die Verwendung von ExoPlayer, da er eine umfassende Reihe von Funktionen bietet, die die meisten Wiedergabeanwendungsfälle abdecken, und sich an alle zusätzlichen Anwendungsfälle anpassen lässt. ExoPlayer abstrahiert außerdem die Fragmentierung von Geräten und Betriebssystemen, sodass Ihr Code im gesamten Android-Ökosystem einheitlich funktioniert. ExoPlayer umfasst:

Auf dieser Seite werden die wichtigsten Schritte zum Erstellen einer Wiedergabe-App beschrieben. Weitere Informationen finden Sie in unseren vollständigen Leitfäden zu Media3 ExoPlayer.

Erste Schritte

Fügen Sie zuerst eine Abhängigkeit von den ExoPlayer-, UI- und Common-Modulen von Jetpack Media3 hinzu:

Kotlin

implementation("androidx.media3:media3-exoplayer:1.11.1")
implementation("androidx.media3:media3-ui:1.11.1")
implementation("androidx.media3:media3-common:1.11.1")

Groovy

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

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

Ersetzen Sie 1.11.1 durch die gewünschte Version der Bibliothek. Die aktuelle Version finden Sie in den Versionshinweisen.

Mediaplayer erstellen

Mit Media3 können Sie entweder die enthaltene Implementierung der Player-Schnittstelle, ExoPlayer, verwenden oder eine eigene benutzerdefinierte Implementierung erstellen.

ExoPlayer erstellen

So erstellen Sie eine ExoPlayer-Instanz am einfachsten:

Kotlin

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

Java

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

Du kannst deinen Mediaplayer in der Lebenszyklusmethode onCreate() der Activity, Fragment oder Service erstellen, in der er sich befindet.

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

Media3 bietet eine PlayerView-UI-Komponente, die Sie in die Layoutdatei Ihrer App einfügen können. Diese Komponente enthält ein PlayerControlView für die Wiedergabesteuerung, ein SubtitleView für die Anzeige von Untertiteln und ein Surface für das Rendern von Videos.

Player wird vorbereitet

Füge einer Playlist Mediendateien für die Wiedergabe hinzu. Verwende dazu Methoden wie setMediaItem() und addMediaItem(). Rufe 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 ist. Wenn sich der Player in einer Activity oder Fragment befindet, bedeutet dies, dass der Player in der Lebenszyklusmethode onStart() auf API-Level 24 und höher oder in der Lebenszyklusmethode onResume() auf API-Level 23 und niedriger vorbereitet wird. Für einen Spieler, der sich in einem Service befindet, kannst du ihn in onCreate() vorbereiten. Ein Beispiel für die Implementierung von Lebenszyklusmethoden finden Sie im ExoPlayer-Codelab.

Player steuern

Nachdem der Player vorbereitet wurde, kannst du die Wiedergabe steuern, indem du Methoden für den Player aufrufst, z. B.:

UI-Komponenten wie PlayerView oder PlayerControlView werden entsprechend aktualisiert, wenn sie an einen Player gebunden sind.

Spieler freigeben

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

Wenn sich der Player in einem Activity oder Fragment befindet, gib ihn in der Lebenszyklusmethode onStop() auf API-Level 24 und höher oder in der Methode onPause() auf API-Level 23 und niedriger frei. Einen Player, der sich in einem Service befindet, können Sie in onDestroy() freigeben. Ein Beispiel für die Implementierung von Lebenszyklusmethoden finden Sie im Exoplayer-Codelab.

Wiedergabe mit einer Media Session verwalten

Unter Android bieten Media Sessions eine standardisierte Möglichkeit, über Prozessgrenzen hinweg mit einem Mediaplayer zu interagieren. Wenn du eine Media Session mit deinem Player verbindest, kannst du die Medienwiedergabe extern bewerben und Wiedergabebefehle von externen Quellen empfangen, z. B. um die Mediensteuerung des Systems auf Mobilgeräten und Geräten mit großem Bildschirm zu integrieren.

Wenn Sie Mediensitzungen verwenden möchten, fügen Sie eine Abhängigkeit vom Media3-Sitzungsmodul hinzu:

Kotlin

implementation("androidx.media3:media3-session:1.11.1")

Groovy

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

Media Session erstellen

So erstellen Sie ein MediaSession-Objekt, nachdem Sie einen Player initialisiert haben:

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 die Kontrolle überlassen

Client-Apps können einen Media Controller implementieren, um die Wiedergabe Ihrer Media Session zu steuern. Wenn Sie diese Anfragen erhalten möchten, legen Sie beim Erstellen des MediaSession ein Callback-Objekt fest.

Wenn ein Controller eine Verbindung zu Ihrer Media Session herstellt, wird die Methode onConnect() aufgerufen. Anhand der bereitgestellten ControllerInfo können Sie entscheiden, ob Sie die Anfrage annehmen oder ablehnen. Ein Beispiel dafür finden Sie in der Media3-Session-Demo-App.

Nachdem eine Verbindung hergestellt wurde, kann ein Controller Wiedergabebefehle an die Session senden. Die Session leitet diese Befehle dann an den Player weiter. Wiedergabe- und Playlist-Befehle, die in der Player-Schnittstelle definiert sind, werden automatisch von der Session verarbeitet.

Mit anderen Callback-Methoden kannst du beispielsweise Anfragen für benutzerdefinierte Wiedergabebefehle und das Ändern der Playlist verarbeiten. Diese Callbacks enthalten ebenfalls ein ControllerInfo-Objekt, sodass Sie die Zugriffssteuerung für jede Anfrage einzeln festlegen können.

Medien im Hintergrund abspielen

Wenn du Medien weiter abspielen möchtest, wenn deine App nicht im Vordergrund ist, z. B. um Musik, Hörbücher oder Podcasts abzuspielen, auch wenn der Nutzer deine App nicht geöffnet hat, sollten deine Player und MediaSession in einem Dienst im Vordergrund gekapselt sein. Media3 bietet hierfür die Schnittstelle MediaSessionService.

MediaSessionService implementieren

Erstelle eine Klasse, die MediaSessionService erweitert, und instanziiere deine 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();
    }
}

Deklarieren Sie in Ihrem Manifest die Klasse Service mit einem Intent-Filter MediaSessionService 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" />

Überschreibe schließlich in der von dir erstellten Klasse die Methode onGetSession(), um den Clientzugriff auf deine Mediensitzung zu steuern. Geben Sie MediaSession zurück, um die Verbindungsanfrage zu akzeptieren, 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 Benutzeroberfläche herstellen

Da sich Ihre Mediensitzung jetzt in einem Service befindet, der sich von der Activity oder dem Fragment unterscheidet, in dem sich die Benutzeroberfläche des Players befindet, können Sie sie mit einem MediaController verknüpfen. Erstellen Sie in der Methode onStart() der Activity oder Fragment mit Ihrer UI einen SessionToken für Ihre MediaSession und verwenden Sie dann den SessionToken, um einen MediaController zu erstellen. Das Erstellen einer 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 du dieselben Methoden wie play() und pause() verwenden kannst, um die Wiedergabe zu steuern. Denken Sie wie bei anderen Komponenten daran, die MediaController-Komponente freizugeben, wenn sie nicht mehr benötigt wird. Rufen Sie dazu in der Lebenszyklusmethode onStop() einer Activity die Methode MediaController.releaseFuture() auf.

Benachrichtigung veröffentlichen

Dienste im Vordergrund müssen eine Benachrichtigung veröffentlichen, während sie aktiv sind. Ein MediaSessionService erstellt automatisch eine MediaStyle-Benachrichtigung in Form eines MediaNotification. Wenn Sie eine benutzerdefinierte Benachrichtigung bereitstellen möchten, erstellen Sie eine MediaNotification.Provider mit DefaultMediaNotificationProvider.Builder oder erstellen Sie eine benutzerdefinierte Implementierung der Anbieterschnittstelle. Fügen Sie Ihren Anbieter mit setMediaNotificationProvider zu Ihrem MediaSession hinzu.

Werbung für Ihre Inhaltsbibliothek

Ein MediaLibraryService baut auf einem MediaSessionService auf, indem Client-Apps die von Ihrer App bereitgestellten Medieninhalte durchsuchen können. Client-Apps implementieren einen MediaBrowser, um mit Ihrem MediaLibraryService zu interagieren.

Die Implementierung eines MediaLibraryService ähnelt der Implementierung eines MediaSessionService, mit dem Unterschied, dass Sie in onGetSession() einen MediaLibrarySession anstelle eines MediaSession zurückgeben sollten. Im Vergleich zu einem MediaSession.Callback enthält der MediaLibrarySession.Callback zusätzliche Methoden, mit denen ein Browserclient die von Ihrem Bibliotheksdienst angebotenen Inhalte durchsuchen kann.

Deklarieren Sie ähnlich wie bei MediaSessionService die 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 alte MediaBrowserService-Version, um die Abwärtskompatibilität zu gewährleisten. Der zusätzliche Intent-Filter ermöglicht es Client-Apps, die die MediaBrowserCompat API verwenden, deine Service zu erkennen.

Mit einem MediaLibrarySession können Sie Ihre Inhaltsbibliothek in einer Baumstruktur mit einem einzigen Stamm-MediaItem bereitstellen. Jeder MediaItem im Baum kann eine beliebige Anzahl von untergeordneten MediaItem-Knoten haben. Je nach Anfrage der Client-App können Sie einen anderen Stamm oder einen anderen Baum bereitstellen. Beispiel: Der Baum, den Sie an einen Client zurückgeben, der nach einer Liste empfohlener Mediendateien sucht, enthält möglicherweise nur den Stammknoten MediaItem und eine einzelne Ebene von untergeordneten Knoten MediaItem, während der Baum, den Sie an eine andere Client-App zurückgeben, eine vollständigere Inhaltsbibliothek darstellen kann.

Ein MediaLibrarySession erstellen

Ein MediaLibrarySession erweitert die MediaSession API, um APIs zum Durchsuchen von Inhalten hinzuzufügen. Im Vergleich zum MediaSession-Callback werden beim MediaLibrarySession-Callback Methoden wie die folgenden hinzugefügt:

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

Relevante Callback-Methoden enthalten ein LibraryParams-Objekt mit zusätzlichen Signalen zum Typ des Inhaltsbaums, an dem eine Client-App interessiert ist.