Einfache Mediaplayer-App mit Media3 ExoPlayer erstellen

Jetpack Media3 definiert eine Player-Schnittstelle, die grundlegende Funktionen für die Wiedergabe von Video- und Audiodateien umreißt. ExoPlayer ist die Standardimplementierung dieser Schnittstelle in Media3. Wir empfehlen die Verwendung von ExoPlayer, da es umfassende Funktionen bietet, die die meisten Anwendungsfälle bei der Wiedergabe abdecken und sich an weitere Anwendungsfälle anpassen lässt. ExoPlayer abstrahiert die Fragmentierung von Geräten und Betriebssystemen, sodass Ihr Code auf der gesamten Android-Plattform einheitlich funktioniert. ExoPlayer enthält:

Auf dieser Seite werden einige der wichtigsten Schritte zum Erstellen einer Wiedergabe-App erläutert. Weitere Informationen findest du in unseren vollständigen Leitfäden für Media3 ExoPlayer.

Erste Schritte

Fügen Sie zuerst eine Abhängigkeit für die ExoPlayer-, UI- und allgemeinen Module 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ötigen Sie möglicherweise 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. In den Versionshinweisen finden Sie die aktuelle Version.

Mediaplayer erstellen

Bei Media3 können Sie entweder die enthaltene Implementierung der Player-Schnittstelle (ExoPlayer) verwenden oder Ihre 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();

Sie können Ihren Mediaplayer in der Lebenszyklusmethode onCreate() der Activity, Fragment oder Service erstellen, in der er sich befindet.

Die Builder bietet verschiedene Anpassungsoptionen, z. B.:

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

Player wird vorbereitet

Du kannst einer Playlist Medienelemente zur Wiedergabe mit Methoden wie setMediaItem() und addMediaItem() hinzufügen. Rufen Sie dann prepare() auf, um mit dem Laden der Medien zu beginnen und die erforderlichen Ressourcen zu erhalten.

Sie sollten diese Schritte nicht ausführen, bevor die App im Vordergrund ausgeführt wird. Wenn sich Ihr Player in einem Activity oder Fragment befindet, bedeutet dies, dass der Player in der Lebenszyklusmethode onStart() auf API-Level 24 und höher oder die Lebenszyklusmethode onResume() auf API-Level 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, kannst du die Wiedergabe durch Aufrufen der folgenden Methoden im Player steuern:

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

Lasse den Player los.

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

Wenn sich dein Player in einem Activity oder Fragment befindet, gib den Player in der Lebenszyklusmethode onStop() auf API-Level 24 und höher oder die Methode onPause() auf API-Level 23 und niedriger frei. Einen Spieler, der sich in einem Service befindet, kannst du in onDestroy() freigeben.

Wiedergabe mit einer Mediensitzung verwalten

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

Um Mediensitzungen zu verwenden, fügen Sie eine Abhängigkeit für das Media3-Sitzungsmodul hinzu:

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

Mediensitzung erstellen

So kannst du nach dem Initialisieren eines Players ein MediaSession 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 automatisch den Status von Player mit dem Status von MediaSession. Dies funktioniert mit jeder Player-Implementierung, einschließlich ExoPlayer und CastPlayer, oder einer benutzerdefinierten Implementierung.

Anderen Kunden Kontrolle gewähren

Client-Apps können einen Mediencontroller implementieren, um die Wiedergabe Ihrer Mediensitzung zu steuern. Damit Sie diese Anfragen erhalten, müssen Sie beim Erstellen von MediaSession ein Callback-Objekt festlegen.

Wenn ein Controller eine Verbindung zu Ihrer Mediensitzung herstellen möchte, wird die Methode onConnect() aufgerufen. Anhand des bereitgestellten ControllerInfo kannst du entscheiden, ob du die Anfrage akzeptieren oder ablehnen möchtest. Ein Beispiel hierfür findest du in der Media3 Session-Demo-App.

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

Mit anderen Callback-Methoden kannst du beispielsweise Anfragen für benutzerdefinierte Wiedergabebefehle oder die Änderung der Playlist verarbeiten. Diese Callbacks enthalten in ähnlicher Weise ein ControllerInfo-Objekt, sodass Sie die Zugriffssteuerung für jede einzelne Anfrage festlegen können.

Wiedergabe von Medien im Hintergrund

Damit die Wiedergabe von Medien fortgesetzt wird, wenn Ihre App nicht im Vordergrund ausgeführt wird, 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 Vordergrunddienst gekapselt sein. Media3 stellt die MediaSessionService-Schnittstelle für diesen Zweck 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 dem Intent-Filter MediaSessionService ein 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 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 wird hergestellt

Da sich die Mediensitzung nun in einem Service befindet, der nicht mit dem Activity oder Fragment verknüpft ist, in dem sich die Benutzeroberfläche des Players befindet, kannst du sie mit einem MediaController verknüpfen. Erstellen Sie in der onStart()-Methode von Activity oder Fragment mit Ihrer UI eine SessionToken für Ihr MediaSession und verwenden Sie dann SessionToken, um ein 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 zur Steuerung der Wiedergabe dieselben Methoden wie play() und pause() verwenden können. Ähnlich wie bei anderen Komponenten sollten Sie MediaController freigeben, wenn sie nicht mehr benötigt werden, z. B. die Lebenszyklusmethode onStop() einer Activity durch Aufrufen von MediaController.releaseFuture().

Benachrichtigungen veröffentlichen

Dienste im Vordergrund sind erforderlich, um eine Benachrichtigung zu veröffentlichen, wenn sie aktiv sind. Durch MediaSessionService wird automatisch eine MediaStyle-Benachrichtigung in Form einer MediaNotification für Sie erstellt. Wenn du eine benutzerdefinierte Benachrichtigung senden möchtest, erstelle eine MediaNotification.Provider mit DefaultMediaNotificationProvider.Builder oder eine benutzerdefinierte Implementierung der Anbieterschnittstelle. Fügen Sie Ihren Anbieter mit setMediaNotificationProvider zu MediaSession hinzu.

Werbung für deine Inhaltsbibliothek

Ein MediaLibraryService baut auf einem MediaSessionService auf und erlaubt Client-Apps, die von deiner App bereitgestellten Medieninhalte zu durchsuchen. Client-Apps implementieren eine MediaBrowser, um mit deinem MediaLibraryService zu interagieren.

Die Implementierung einer MediaLibraryService ähnelt der Implementierung eines MediaSessionService, mit der Ausnahme, dass Sie in onGetSession() ein MediaLibrarySession anstelle eines MediaSession zurückgeben sollten. Im Vergleich zu MediaSession.Callback enthält MediaLibrarySession.Callback zusätzliche Methoden, mit denen ein Browserclient durch die Inhalte Ihres Bibliotheksdienstes navigieren kann.

Ähnlich wie bei MediaSessionService deklarieren Sie MediaLibraryService in Ihrem Manifest und fordern 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 die MediaLibraryService und aus Gründen der Abwärtskompatibilität für das alte MediaBrowserService. Durch den zusätzlichen Intent-Filter können Clientanwendungen, die die MediaBrowserCompat API verwenden, Ihre 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. Je nach Anfrage der Clientanwendung können Sie einen anderen Stamm oder einen anderen Baum bereitstellen. Beispiel: Die Baumstruktur, die Sie an einen Client zurückgeben, der nach einer Liste empfohlener Medienelemente sucht, enthält möglicherweise nur den Stamm MediaItem und eine einzelne Ebene von untergeordneten MediaItem-Knoten, während die Baumstruktur, die Sie zu einer anderen Clientanwendung zurückgeben, eine umfassendere Inhaltsbibliothek darstellt.

MediaLibrarySession wird erstellt

Ein MediaLibrarySession erweitert die MediaSession API um APIs zum Durchsuchen von Inhalten. Im Vergleich zum MediaSession-Callback fügt der MediaLibrarySession-Callback folgende Methoden hinzu:

  • onGetLibraryRoot() für den Fall, dass ein Client den MediaItem-Stamm einer Inhaltsstruktur anfordert
  • onGetChildren() für den Fall, dass ein Client die untergeordneten Elemente von MediaItem in der Inhaltsstruktur anfordert
  • onGetSearchResult(), wenn ein Client Suchergebnisse aus der Inhaltsstruktur für eine bestimmte Abfrage anfordert

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