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 er 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 einheitlich funktioniert. ExoPlayer umfasst:

Auf dieser Seite werden einige der 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:

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

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

Ersetzen Sie 1.10.0 durch die gewünschte Version der Bibliothek. Die neueste 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 Mediaplayer in der Lebenszyklusmethode onCreate() der Activity, des Fragment oder des Service erstellen, in dem er sich befindet.

Der Builder enthält eine Reihe von Anpassungsoptionen, die für Sie interessant sein könnten, z. B.:

Media3 bietet eine UI-Komponente PlayerView, die Sie in die Layoutdatei Ihrer App einfügen können. Diese Komponente kapselt eine PlayerControlView für die Wiedergabesteuerung, eine SubtitleView zum Anzeigen von Untertiteln und eine Surface zum Rendern von Videos.

Player vorbereiten

Fügen Sie einer Playlist Medienelemente zur Wiedergabe mit Methoden wie setMediaItem() und addMediaItem() hinzu. Rufen Sie dann prepare() auf, um das Laden von Medien zu starten und die erforderlichen Ressourcen abzurufen.

Diese Schritte sollten erst ausgeführt werden, wenn sich die App im Vordergrund befindet. Wenn sich Ihr Player in einer Activity oder einem Fragment befindet, bedeutet dies, dass Sie den Player in der Lebenszyklusmethode onStart() auf API-Level 24 und höher oder in der Lebenszyklusmethode onResume() auf API-Level 23 und niedriger vorbereiten müssen. Bei einem Player, der sich in einem Service befindet, können Sie ihn in onCreate() vorbereiten. Ein Beispiel für die Implementierung von Lebenszyklusmethoden finden Sie im ExoPlayer-Codelab.

Player steuern

Nachdem der Player vorbereitet wurde, können Sie die Wiedergabe steuern, indem Sie Methoden auf dem Player aufrufen, z. B.:

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

Player freigeben

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

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

Wiedergabe mit einer Media Session verwalten

In Android bieten Media Sessions eine standardisierte Möglichkeit, über Prozessgrenzen hinweg mit einem Mediaplayer zu interagieren. Wenn Sie eine Media Session mit Ihrem Player verbinden können Sie die Medienwiedergabe extern bewerben und Wiedergabebefehle von externen Quellen empfangen, z. B. um sie in die Systemmediensteuerung auf Mobilgeräten und Geräten mit großem Bildschirm zu integrieren.

Wenn Sie Media Sessions verwenden möchten, fügen Sie eine Abhängigkeit vom Media3 Session-Modul hinzu:

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

Media Session erstellen

Sie können eine MediaSession erstellen, nachdem Sie einen Player wie folgt 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 des Player automatisch mit dem Status der 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 empfangen möchten, legen Sie beim Erstellen Ihrer MediaSession ein Callback-Objekt fest.

Wenn ein Controller eine Verbindung zu Ihrer Media Session herstellt, wird die onConnect() Methode 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 können Sie beispielsweise Anfragen für benutzerdefinierte Wiedergabebefehle und das Ändern der Playlistverarbeiten. Diese Callbacks enthalten ebenfalls ein ControllerInfo-Objekt, sodass Sie die Zugriffssteuerung auf Anfragebasis festlegen können.

Medien im Hintergrund abspielen

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

MediaSessionService implementieren

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

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 Ihre Service Klasse 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 Media Session zu steuern. Geben Sie eine MediaSession zurück, um die Verbindungsanfrage anzunehmen, oder geben Sie null zurück, 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 Media Session jetzt in einem Service befindet, der von der Activity oder dem Fragment getrennt ist, in dem sich die Player-UI befindet, können Sie sie mit einem MediaController verknüpfen. Erstellen Sie in der Methode onStart() der Activity oder des Fragment mit Ihrer UI ein SessionToken für Ihre MediaSession und verwenden Sie dann das 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 dieselben Methoden wie play() und pause() verwenden können, um die Wiedergabe zu steuern. Ähnlich wie bei anderen Komponenten müssen Sie den MediaController freigeben, wenn er nicht mehr benötigt wird, z. B. in der onStop() Lebenszyklusmethode einer Activity, indem Sie MediaController.releaseFuture() aufrufen.

Benachrichtigung veröffentlichen

Dienste im Vordergrund müssen eine Benachrichtigung veröffentlichen, während sie aktiv sind. A MediaSessionService erstellt automatisch eine MediaStyle Benachrichtigung für Sie in Form einer MediaNotification. Wenn Sie eine benutzerdefinierte Benachrichtigung bereitstellen möchten, erstellen Sie ein MediaNotification.Provider mit DefaultMediaNotificationProvider.Builder oder indem Sie eine benutzerdefinierte Implementierung der Provider-Schnittstelle erstellen. Fügen Sie Ihren Provider zu Ihrer MediaSession mit setMediaNotificationProvider hinzu.

Inhaltsbibliothek bewerben

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

Die Implementierung eines MediaLibraryService ähnelt der Implementierung eines MediaSessionService. Der Unterschied besteht darin, dass Sie in onGetSession() eine MediaLibrarySession anstelle einer 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.

Ähnlich wie beim MediaSessionService deklarieren Sie 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 für den MediaLibraryService und aus Gründen der Abwärtskompatibilität auch für den älteren MediaBrowserService. Der zusätzliche Intent-Filter ermöglicht es Client-Apps, die die MediaBrowserCompat-API verwenden, Ihren Service zu erkennen.

Mit einer MediaLibrarySession können Sie Ihre Inhaltsbibliothek in einer Baumstruktur mit einem einzelnen Stamm-MediaItem bereitstellen. Jedes MediaItem im Baum kann eine beliebige Anzahl untergeordneter MediaItem-Knoten haben. Sie können je nach Anfrage der Client-App einen anderen Stamm oder einen anderen Baum bereitstellen. Beispielsweise enthält der Baum, den Sie an einen Client zurückgeben, der nach einer Liste empfohlener Medienelemente sucht, möglicherweise nur den Stamm-MediaItem und eine einzelne Ebene untergeordneter MediaItem-Knoten. Der Baum, den Sie an eine andere Client-App zurückgeben, kann jedoch eine vollständigere Inhaltsbibliothek darstellen.

MediaLibrarySession erstellen

Eine MediaLibrarySession erweitert die MediaSession-API um APIs zum Durchsuchen von Inhalten. Im Vergleich zum MediaSession Callback, fügt der MediaLibrarySession Callback Methoden wie die folgenden hinzu:

  • onGetLibraryRoot() für den Fall, dass ein Client das Stamm-MediaItem eines Inhaltsbaums anfordert
  • onGetChildren() für den Fall, dass ein Client die untergeordneten Elemente eines MediaItem im Inhaltsbaum anfordert
  • onGetSearchResult() für den Fall, dass 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.