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 Wiedergabeanwendungsfälle abdecken, und an alle zusätzlichen Anwendungsfälle angepasst werden kann. ExoPlayer abstrahiert auch die Geräte- und Betriebssystemfragmentierung, sodass Ihr Code im gesamten Android-Ökosystem konsistent funktioniert. ExoPlayer umfasst:

Auf dieser Seite werden einige der wichtigsten Schritte beim Erstellen einer Wiedergabe-App beschrieben. Weitere Informationen finden Sie in unseren vollständigen Anleitungen zu Media3 ExoPlayer.

Erste Schritte

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

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

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

Ersetzen Sie 1.7.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

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 Media Player in der Methode onCreate() des Lebenszyklus 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 umfasst ein PlayerControlView für die Wiedergabesteuerung, ein SubtitleView für die Anzeige von Untertiteln und ein Surface für das Rendern von Videos.

Player vorbereiten

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

Sie sollten diese Schritte erst ausführen, wenn die App im Vordergrund ist. Wenn sich dein Player in einem Activity oder Fragment befindet, bedeutet das, dass der Player in der onStart()-Lebenszyklusmethode auf API-Level 24 und höher oder in der onResume()-Lebenszyklusmethode auf API-Level 23 und niedriger vorbereitet wird. Für einen Spieler, der sich in einem Service befindet, können Sie ihn in onCreate() vorbereiten.

Player steuern

Nachdem der Player vorbereitet wurde, können Sie die Wiedergabe steuern, indem Sie Methoden für den Player aufrufen, z. B.:

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

Spieler freigeben

Die Wiedergabe kann Ressourcen erfordern, 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, geben Sie ihn mit der onStop()-Lebenszyklusmethode auf API-Level 24 und höher oder mit der onPause()-Methode auf API-Level 23 und niedriger frei. Ein Spieler, der sich in einem Service befindet, kann in onDestroy() veröffentlicht werden.

Wiedergabe mit einer Mediensitzung verwalten

Unter Android bieten Mediensitzungen eine standardisierte Möglichkeit, prozessübergreifend mit einem Mediaplayer zu interagieren. Wenn Sie eine Mediensitzung mit Ihrem Player verbinden, können Sie Ihre Medienwiedergabe extern bewerben und Wiedergabebefehle von externen Quellen empfangen, z. B. um die System-Mediensteuerung auf Mobilgeräten und Großbildgeräten zu integrieren.

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

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

Mediensitzung erstellen

So erstellen Sie ein MediaSession-Objekt nach der Initialisierung eines Players:

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. Das funktioniert mit jeder Player-Implementierung, einschließlich ExoPlayer, CastPlayer oder einer benutzerdefinierten Implementierung.

Anderen Kunden die Kontrolle überlassen

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

Wenn ein Controller eine Verbindung zu Ihrer Mediensitzung 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.

Sobald eine Verbindung hergestellt ist, kann ein Controller Wiedergabebefehle an die Sitzung senden. Die Sitzung delegiert diese Befehle dann an den Player. Wiedergabe- und Playlist-Befehle, die in der Player-Schnittstelle definiert sind, werden automatisch von der Sitzung verarbeitet.

Mit anderen Callback-Methoden können Sie beispielsweise Anfragen für benutzerdefinierte Wiedergabebefehle und Änderungen an 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 Sie Medien weiter abspielen möchten, wenn Ihre App nicht im Vordergrund ist, z. B. um Musik, Hörbücher oder Podcasts abzuspielen, auch wenn der Nutzer Ihre App nicht geöffnet hat, sollten Ihre Player und MediaSession in einem Dienst im Vordergrund gekapselt werden. Media3 bietet dafür die MediaSessionService-Schnittstelle.

MediaSessionService implementieren

Erstellen Sie eine Klasse, die MediaSessionService erweitert, und instanziieren Sie MediaSession in der Lifecycle-Methode 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();
    }
}

Geben Sie in Ihrem Manifest die Klasse Service mit einem Intent-Filter MediaSessionService an und fordern Sie die Berechtigung FOREGROUND_SERVICE an, um einen Vordergrunddienst 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. Gib MediaSession zurück, um die Verbindungsanfrage anzunehmen, oder null, um sie 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, das sich von Activity oder Fragment unterscheidet, in dem sich die Benutzeroberfläche Ihres Players befindet, können Sie ein MediaController verwenden, um sie zu verknüpfen. Erstellen Sie in der Methode onStart() der Activity oder Fragment mit Ihrer Benutzeroberfläche einen SessionToken für Ihren MediaSession und verwenden Sie dann den 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. Sie können also dieselben Methoden wie play() und pause() verwenden, um die Wiedergabe zu steuern. Wie bei anderen Komponenten sollten Sie die MediaController freigeben, wenn sie nicht mehr benötigt wird, z. B. in der onStop()-Lifecycle-Methode eines Activity, indem Sie MediaController.releaseFuture() aufrufen.

Benachrichtigung veröffentlichen

Für Dienste im Vordergrund ist es erforderlich, dass sie eine Benachrichtigung veröffentlichen, während sie aktiv sind. Bei einer MediaSessionService wird automatisch eine MediaStyle-Benachrichtigung in Form einer MediaNotification erstellt. Wenn Sie eine benutzerdefinierte Benachrichtigung bereitstellen möchten, erstellen Sie ein 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

Eine MediaLibraryService baut auf einer MediaSessionService auf, indem Client-Apps die von Ihrer App bereitgestellten Medieninhalte durchsuchen können. Client-Apps implementieren eine MediaBrowser, um mit Ihrer MediaLibraryService zu interagieren.

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

Deklarieren Sie 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 Beispiel oben enthält einen Intent-Filter für MediaLibraryService und zur Abwärtskompatibilität auch für das alte MediaBrowserService. Der zusätzliche Intent-Filter ermöglicht es Client-Apps, die die MediaBrowserCompat API verwenden, Ihre Service zu erkennen.

Mit einem MediaLibrarySession können Sie Ihre Inhaltsbibliothek in einer Baumstruktur mit einem einzelnen Stammverzeichnis MediaItem bereitstellen. Jedes MediaItem im Baum kann beliebig viele untergeordnete MediaItem-Knoten haben. Sie können je nach Anfrage der Client-App einen anderen Stamm oder eine andere Struktur bereitstellen. Der Baum, den Sie an einen Client zurückgeben, der eine Liste empfohlener Media-Elemente sucht, enthält möglicherweise nur den Stammknoten MediaItem und eine einzelne Ebene mit untergeordneten MediaItem-Knoten. Der Baum, den Sie an eine andere Client-App zurückgeben, kann dagegen eine vollständigere Inhaltsbibliothek darstellen.

MediaLibrarySession erstellen

Eine MediaLibrarySession erweitert die MediaSession API um APIs zum Durchsuchen von Inhalten. Im Vergleich zum MediaSession-Callback werden dem 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 von MediaItem im Inhaltsbaum anfordert
  • onGetSearchResult() wenn ein Kunde Suchergebnisse aus dem Inhaltsbaum für eine bestimmte Anfrage anfordert

Relevante Callback-Methoden enthalten ein LibraryParams-Objekt mit zusätzlichen Signalen zum Typ des Inhaltsbaums, für den sich eine Client-App interessiert.