Einfache Mediaplayer-App mit Media3 ExoPlayer erstellen

Jetpack Media3 definiert eine Player-Schnittstelle, die die grundlegenden 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 Palette von Funktionen bietet, die die meisten Wiedergabeanwendungsfälle abdecken, und sich für zusätzliche Anwendungsfälle anpassen lässt. ExoPlayer abstrahiert auch die Geräte- und Betriebssystemfragmentierung, sodass Ihr Code im gesamten Android-System konsistent funktioniert. ExoPlayer umfasst Folgendes:

Auf dieser Seite werden einige der wichtigsten Schritte zum Erstellen einer Wiedergabe-App beschrieben. Weitere Informationen findest du in unseren vollständigen Anleitungen zum 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.4.1"
implementation "androidx.media3:media3-ui:1.4.1"
implementation "androidx.media3:media3-common:1.4.1"

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

Ersetzen Sie 1.4.1 durch die gewünschte Version der Bibliothek. Informationen zur neuesten Version finden Sie in den Versionshinweisen.

Mediaplayer erstellen

Mit Media3 kannst du entweder die integrierte Implementierung der Player-Schnittstelle, ExoPlayer, verwenden oder eine eigene benutzerdefinierte Implementierung erstellen.

ExoPlayer erstellen

So erstellen Sie am einfachsten eine ExoPlayer-Instanz:

Kotlin

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

Java

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

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

Die Builder bietet eine Reihe von Anpassungsoptionen, z. B.:

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

Player vorbereiten

Fügen Sie einer Playlist Medienelemente zur Wiedergabe mit Methoden wie setMediaItem() und addMediaItem() hinzu. Rufe dann prepare() auf, um mit dem Laden der Medien zu beginnen und die erforderlichen Ressourcen zu erwerben.

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

Player steuern

Nachdem der Player vorbereitet wurde, kannst du die Wiedergabe steuern, indem du Methoden auf dem Player aufrufst, z. B.:

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

Spieler freigeben

Für die Wiedergabe können Ressourcen benötigt werden, die nur begrenzt verfügbar sind, z. B. Videodekoder. Daher ist es wichtig, release() auf dem Player aufzurufen, um Ressourcen freizugeben, wenn der Player nicht mehr benötigt wird.

Wenn sich dein Player in einem Activity oder Fragment befindet, veröffentliche ihn mit der onStop()-Lebenszyklusmethode auf API-Level 24 und höher oder der onPause()-Methode auf API-Level 23 und niedriger. Wenn sich ein Spieler in einer Service befindet, kannst du ihn in onDestroy() veröffentlichen.

Wiedergabe mit einer Mediensitzung verwalten

Auf Android-Geräten bieten Mediensitzungen eine standardisierte Möglichkeit, über Prozessgrenzen hinweg mit einem Mediaplayer zu interagieren. Wenn du eine Mediensitzung mit deinem Player verbindest, kannst du deine Medienwiedergabe extern bewerben und Wiedergabebefehle von externen Quellen empfangen. So kannst du sie beispielsweise in die Systemmediensteuerung auf Mobilgeräten und Geräten mit großem Bildschirm einbinden.

Wenn du Mediensitzungen verwenden möchtest, musst du eine Abhängigkeit vom Media3-Sitzungsmodul hinzufügen:

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

Mediensitzung erstellen

So erstellst du eine MediaSession 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 der Player automatisch mit dem Status der MediaSession. Das funktioniert mit jeder Player-Implementierung, einschließlich ExoPlayer, CastPlayer oder einer benutzerdefinierten Implementierung.

Andere Kunden verwalten lassen

Client-Apps können einen Mediencontroller implementieren, um die Wiedergabe der Mediensitzung zu steuern. Wenn du diese Anfragen erhalten möchtest, musst du beim Erstellen deiner MediaSession ein Callback-Objekt festlegen.

Wenn ein Controller eine Verbindung zu deiner Mediensitzung herstellen möchte, wird die Methode onConnect() aufgerufen. Mithilfe des bereitgestellten ControllerInfo können Sie entscheiden, ob Sie die Anfrage annehmen oder ablehnen. Ein Beispiel dafür findest du in der Media3 Session-Demo-App.

Nach der Verbindung kann ein Controller Wiedergabebefehle an die Sitzung senden. Die Sitzung delegiert diese Befehle dann an den Player. Wiedergabe- und Playlistbefehle, die in der Player-Benutzeroberfläche definiert sind, werden automatisch von der Sitzung verarbeitet.

Mit anderen Rückrufmethoden kannst du beispielsweise Anfragen für benutzerdefinierte Wiedergabebefehle und Änderungen an der Playlist verarbeiten. Diese Callbacks enthalten ebenfalls ein ControllerInfo-Objekt, damit du die Zugriffssteuerung für jede Anfrage einzeln festlegen kannst.

Medien im Hintergrund abspielen

Wenn Sie Medien auch dann weiter abspielen möchten, wenn Ihre App nicht im Vordergrund ist, z. B. Musik, Hörbücher oder Podcasts, auch wenn der Nutzer Ihre App nicht geöffnet hat, sollten Player und MediaSession in einem Dienst im Vordergrund gekapselt werden. Media3 stellt hierfür die MediaSessionService-Schnittstelle 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 Service-Klasse mit einem MediaSessionService-Intent-Filter hinzu und fordern Sie die Berechtigung FOREGROUND_SERVICE zum Ausführen eines Dienstes im Vordergrund an:

<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 abschließend 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 deine Mediensitzung jetzt in einer Service befindet, die von der Activity oder Fragment getrennt ist, in der sich die Player-UI befindet, kannst du sie mithilfe einer MediaController verknüpfen. Erstellen Sie in der onStart()-Methode der Activity oder Fragment mit Ihrer UI eine SessionToken für Ihre MediaSession und verwenden Sie dann die SessionToken, um eine 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-Benutzeroberfläche. Du kannst also die Wiedergabe mit denselben Methoden wie play() und pause() steuern. Ähnlich wie bei anderen Komponenten müssen Sie die MediaController freigeben, wenn sie nicht mehr benötigt wird, z. B. die 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. Mit einer MediaSessionService wird automatisch eine MediaStyle-Benachrichtigung in Form einer MediaNotification für Sie erstellt. Wenn Sie eine benutzerdefinierte Benachrichtigung bereitstellen möchten, erstellen Sie eine MediaNotification.Provider mit DefaultMediaNotificationProvider.Builder oder 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 sie Client-Apps ermöglicht, die von Ihrer App bereitgestellten Medieninhalte zu durchsuchen. Client-Apps implementieren eine MediaBrowser, um mit Ihrer MediaLibraryService zu interagieren.

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

Deklarieren Sie die MediaLibraryService ähnlich wie die MediaSessionService in Ihrem Manifest und fordern Sie die Berechtigung FOREGROUND_SERVICE zum Ausführen eines Dienstes im Vordergrund an:

<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 sowohl für MediaLibraryService als auch für den alten MediaBrowserService, um die Abwärtskompatibilität zu gewährleisten. Mit dem zusätzlichen Intent-Filter können Client-Apps, die die MediaBrowserCompat API verwenden, Ihre Service erkennen.

Mit einem MediaLibrarySession kannst du deine Inhaltsbibliothek in einer Baumstruktur mit einer einzelnen StammMediaItem bereitstellen. Jeder MediaItem im Baum kann beliebig viele untergeordnete 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 du einem Client zurückgibst, der nach einer Liste empfohlener Medienelemente sucht, möglicherweise nur den Stammknoten MediaItem und eine einzige Ebene von untergeordneten Knoten MediaItem, während der Baum, den du einer anderen Client-App zurückgibst, eine vollständigere Bibliothek mit Inhalten darstellt.

MediaLibrarySession erstellen

Eine MediaLibrarySession erweitert die MediaSession API um APIs für die Inhaltssuche. Im Vergleich zum MediaSession-Callback bietet der MediaLibrarySession-Callback Methoden wie:

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

Relevante Rückrufmethoden enthalten ein LibraryParams-Objekt mit zusätzlichen Signalen zur Art des Inhaltsbaums, für den sich eine Client-App interessiert.