Migrationsanleitung für AndroidX Media3

Apps, die derzeit die eigenständige com.google.android.exoplayer2-Bibliothek und androidx.media verwenden, sollten zu androidx.media3 migrieren. Verwenden Sie das Migrationsskript, um Gradle-Build-Dateien, Java- und Kotlin-Quelldateien sowie XML-Layoutdateien von ExoPlayer 2.19.1 zu AndroidX Media3 1.1.1 zu migrieren.

Übersicht

Lesen Sie sich vor der Migration die folgenden Abschnitte durch, um mehr über die Vorteile der neuen APIs, die zu migrierenden APIs und die Voraussetzungen zu erfahren, die das Projekt Ihrer App erfüllen sollte.

Warum zu Jetpack Media3 migrieren?

  • Es ist das neue Zuhause von ExoPlayer, während com.google.android.exoplayer2 eingestellt wird.
  • Zugriff auf die Player API über Komponenten/Prozesse hinweg mit MediaBrowser/MediaController.
  • Verwenden Sie die erweiterten Funktionen der MediaSession- und MediaController-API.
  • Wiedergabefunktionen mit detaillierter Zugriffssteuerung bewerben.
  • Vereinfachen Sie Ihre App, indem Sie MediaSessionConnector und PlayerNotificationManager entfernen.
  • Abwärtskompatibel mit media-compat-Client-APIs (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

Media APIs zur Migration zu AndroidX Media3

  • ExoPlayer und seine Erweiterungen
    Dazu gehören alle Module des alten ExoPlayer-Projekts mit Ausnahme des Moduls mediasession, das eingestellt wurde. Apps oder Module, die von Paketen in com.google.android.exoplayer2 abhängen, können mit dem Migrationsskript migriert werden.
  • MediaSessionConnector (abhängig von den androidx.media.*-Paketen von androidx.media:media:1.4.3+)
    Entferne den MediaSessionConnector und verwende stattdessen den androidx.media3.session.MediaSession.
  • MediaBrowserServiceCompat (abhängig von den androidx.media.*-Paketen von androidx.media:media:1.4.3+)
    Migriere Unterklassen von androidx.media.MediaBrowserServiceCompat zu androidx.media3.session.MediaLibraryService und Code, der MediaBrowserCompat.MediaItem verwendet, zu androidx.media3.common.MediaItem.
  • MediaBrowserCompat (abhängig von den android.support.v4.media.*-Paketen von androidx.media:media:1.4.3+)
    Migriere den Clientcode mit MediaBrowserCompat oder MediaControllerCompat, um androidx.media3.session.MediaBrowser mit androidx.media3.common.MediaItem zu verwenden.

Vorbereitung

  1. Projekt unter Quellcodeverwaltung stellen

    Sorgen Sie dafür, dass Sie Änderungen, die durch skriptbasierte Migrationstools vorgenommen wurden, einfach rückgängig machen können. Wenn Sie Ihr Projekt noch nicht unter Versionsverwaltung haben, ist jetzt ein guter Zeitpunkt, damit zu beginnen. Wenn Sie das aus irgendeinem Grund nicht tun möchten, erstellen Sie eine Sicherungskopie Ihres Projekts, bevor Sie mit der Migration beginnen.

  2. App aktualisieren

    • Wir empfehlen, Ihr Projekt auf die neueste Version der ExoPlayer-Bibliothek zu aktualisieren und alle Aufrufe von verworfenen Methoden zu entfernen. Wenn Sie das Skript für die Migration verwenden möchten, müssen Sie die Version, auf die Sie aktualisieren, mit der vom Skript verarbeiteten Version abgleichen.

    • Erhöhen Sie die compileSdkVersion Ihrer App auf mindestens 32.

    • Aktualisieren Sie Gradle und das Android Studio Gradle-Plug-in auf eine aktuelle Version, die mit den oben genannten aktualisierten Abhängigkeiten funktioniert. Beispiel:

      • Android-Gradle-Plug-in-Version: 7.1.0
      • Gradle-Version: 7.4
    • Ersetzen Sie alle Importanweisungen mit Platzhaltern, die ein Sternchen (*) verwenden, und verwenden Sie vollständig qualifizierte Importanweisungen: Löschen Sie die Importanweisungen mit Platzhaltern und verwenden Sie Android Studio, um die vollständig qualifizierten Anweisungen zu importieren (F2 – Alt/Eingabe, F2 – Alt/Eingabe, ...).

    • Von com.google.android.exoplayer2.PlayerView zu com.google.android.exoplayer2.StyledPlayerView migrieren. Das ist erforderlich, weil es in AndroidX Media3 kein Äquivalent zu com.google.android.exoplayer2.PlayerView gibt.

ExoPlayer mit Scriptunterstützung migrieren

Das Script erleichtert den Wechsel von com.google.android.exoplayer2 zur neuen Paket- und Modulstruktur unter androidx.media3. Das Script führt einige Validierungsprüfungen für Ihr Projekt durch und gibt Warnungen aus, wenn die Validierung fehlschlägt. Andernfalls werden die Zuordnungen umbenannter Klassen und Pakete in den Ressourcen eines Android-Gradle-Projekts angewendet, das in Java oder Kotlin geschrieben wurde.

usage: ./media3-migration.sh [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]
 PROJECT_ROOT: path to your project root (location of 'gradlew')
 -p: list package mappings and then exit
 -c: list class mappings (precedence over package mappings) and then exit
 -d: list dependency mappings and then exit
 -l: list files that will be considered for rewrite and then exit
 -x: exclude the path from the list of file to be changed: 'app/src/test'
 -m: migrate packages, classes and dependencies to AndroidX Media3
 -f: force the action even when validation fails
 -v: print the exoplayer2/media3 version strings of this script
 -h, --help: show this help text

Migrationsskript verwenden

  1. Laden Sie das Migrationsskript aus dem Tag des ExoPlayer-Projekts auf GitHub herunter, das der Version entspricht, auf die Sie Ihre App aktualisiert haben:

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. Machen Sie das Skript ausführbar:

    chmod 744 media3-migration.sh
    
  3. Führen Sie das Skript mit --help aus, um mehr über die Optionen zu erfahren.

  4. Führen Sie das Skript mit -l aus, um die für die Migration ausgewählten Dateien aufzulisten. Verwenden Sie -f, um die Auflistung ohne Warnungen zu erzwingen:

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. Führe das Script mit -m aus, um Pakete, Klassen und Module Media3 zuzuordnen. Wenn Sie das Script mit der Option -m ausführen, werden die Änderungen auf die ausgewählten Dateien angewendet.

    • Bei Validierungsfehler anhalten, ohne Änderungen vorzunehmen
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • Erzwungene Ausführung

    Wenn das Script einen Verstoß gegen die Voraussetzungen findet, kann die Migration mit dem Flag -f erzwungen werden:

    ./media3-migration.sh -m -f /path/to/gradle/project/root
    
 # list files selected for migration when excluding paths
 ./media3-migration.sh -l -x "app/src/test/" -x "service/" /path/to/project/root
 # migrate the selected files
 ./media3-migration.sh -m -x "app/src/test/" -x "service/" /path/to/project/root

Führen Sie die folgenden manuellen Schritte aus, nachdem Sie das Skript mit der Option -m ausgeführt haben:

  1. Prüfen Sie, wie das Skript Ihren Code geändert hat: Verwenden Sie ein Diff-Tool und beheben Sie potenzielle Probleme. Wenn Sie der Meinung sind, dass das Skript ein allgemeines Problem hat, das ohne die Option -f eingeführt wurde, sollten Sie einen Fehler melden.
  2. Projekt erstellen: Verwenden Sie entweder ./gradlew clean build oder wählen Sie in Android Studio Datei > Projekt mit Gradle-Dateien synchronisieren, dann Build > Projekt bereinigen und dann Build > Projekt neu erstellen aus. Sie können den Build auf dem Tab Build – Build-Ausgabe in Android Studio im Blick behalten.

Empfohlene Folgeschritte:

  1. Beheben Sie Fehler bei der Verwendung instabiler APIs.
  2. Verworfene API-Aufrufe ersetzen: Verwenden Sie die vorgeschlagene Ersatz-API. Bewegen Sie den Mauszeiger in Android Studio auf die Warnung und rufen Sie die JavaDoc des verworfenen Symbols auf, um herauszufinden, was Sie stattdessen verwenden sollten.
  3. Importanweisungen sortieren: Öffnen Sie das Projekt in Android Studio, klicken Sie im Projekt-Viewer mit der rechten Maustaste auf einen Paketordnerknoten und wählen Sie für die Pakete, die die geänderten Quelldateien enthalten, Optimize imports aus.

MediaSessionConnector durch androidx.media3.session.MediaSession ersetzen

In der alten MediaSessionCompat-Welt war der MediaSessionConnector dafür verantwortlich, den Status des Players mit dem Status der Sitzung zu synchronisieren und Befehle von Controllern zu empfangen, die an entsprechende Player-Methoden delegiert werden mussten. Mit AndroidX Media3 wird dies direkt von MediaSession erledigt, ohne dass ein Connector erforderlich ist.

  1. Alle Verweise auf und die Verwendung von MediaSessionConnector entfernen:Wenn Sie das automatisierte Skript zum Migrieren von ExoPlayer-Klassen und -Paketen verwendet haben, hat das Skript Ihren Code wahrscheinlich in einem nicht kompilierbaren Zustand in Bezug auf die MediaSessionConnector hinterlassen, die nicht aufgelöst werden können. In Android Studio wird der fehlerhafte Code angezeigt, wenn Sie versuchen, die App zu erstellen oder zu starten.

  2. Fügen Sie in der Datei build.gradle, in der Sie Ihre Abhängigkeiten verwalten, eine Implementierungsabhängigkeit zum AndroidX Media3-Sitzungsmodul hinzu und entfernen Sie die alte Abhängigkeit:

    implementation "androidx.media3:media3-session:1.11.1"
    
  3. Ersetzen Sie MediaSessionCompat durch androidx.media3.session.MediaSession.

  4. Verwenden Sie am Code-Standort, an dem Sie die Legacy-MediaSessionCompat erstellt haben, androidx.media3.session.MediaSession.Builder, um eine MediaSession zu erstellen. Übergeben Sie den Player, um den Session Builder zu erstellen.

    Kotlin

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player).setCallback(MySessionCallback()).build()

    Java

    ExoPlayer player = new ExoPlayer.Builder(context).build();
    mediaSession =
        new MediaSession.Builder(context, player).setCallback(new MySessionCallback()).build();

  5. Implementieren Sie MySessionCallback, wie es für Ihre App erforderlich ist. Dies ist optional. Wenn du Controllern erlauben möchtest, dem Player Mediendateien hinzuzufügen, implementiere MediaSession.Callback.onAddMediaItems(). Sie stellt verschiedene aktuelle und ältere API-Methoden bereit, mit denen Mediendateien abwärtskompatibel zur Wiedergabe im Player hinzugefügt werden können. Dazu gehören die MediaController.set/addMediaItems()-Methoden des Media3-Controllers sowie die TransportControls.prepareFrom*/playFrom*-Methoden der Legacy API. Eine Beispielimplementierung von onAddMediaItems finden Sie in der PlaybackService der Session-Demo-App.

  6. Gib die Mediensitzung an der Codestelle frei, an der du die Sitzung vor der Migration gelöscht hast:

    Kotlin

    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }

    Java

    if (mediaSession != null) {
      mediaSession.getPlayer().release();
      mediaSession.release();
      mediaSession = null;
    }

MediaSessionConnector-Funktionen in Media3

In der folgenden Tabelle sind die Media3 APIs aufgeführt, die Funktionen verarbeiten, die zuvor in MediaSessionConnector implementiert wurden.

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() wird intern aufgerufen)
QueueNavigator ForwardingSimpleBasePlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

MediaBrowserService zu MediaLibraryService migrieren

AndroidX Media3 führt MediaLibraryService ein, das MediaBrowserServiceCompat ersetzt. Das JavaDoc von MediaLibraryService und seiner übergeordneten Klasse MediaSessionService bietet eine gute Einführung in die API und das asynchrone Programmiermodell des Dienstes.

MediaLibraryService ist abwärtskompatibel mit MediaBrowserService. Eine Clientanwendung, die MediaBrowserCompat oder MediaControllerCompat verwendet, funktioniert weiterhin ohne Codeänderungen, wenn eine Verbindung zu einem MediaLibraryService hergestellt wird. Für einen Client ist es transparent, ob Ihre Anwendung eine MediaLibraryService oder eine Legacy-MediaBrowserServiceCompat verwendet.

Diagramm der App-Komponenten mit Dienst, Aktivität und externen Apps.
Abbildung 1: Übersicht über die Komponenten einer Medien-App
  1. Damit die Abwärtskompatibilität funktioniert, müssen Sie beide Dienstschnittstellen mit Ihrem Dienst in der AndroidManifest.xml registrieren. So findet ein Client Ihren Dienst über die erforderliche Dienstschnittstelle:

    <service android:name=".MusicService" android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaLibraryService"/>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    
  2. Fügen Sie in der Datei build.gradle, in der Sie Ihre Abhängigkeiten verwalten, eine Implementierungsabhängigkeit zum AndroidX Media3-Sitzungsmodul hinzu und entfernen Sie die alte Abhängigkeit:

    implementation "androidx.media3:media3-session:1.11.1"
    
  3. Ändern Sie Ihren Dienst so, dass er von einem MediaLibraryService anstelle von MediaBrowserService erbt. Wie bereits erwähnt, ist der MediaLibraryService mit dem Legacy-MediaBrowserService kompatibel. Dementsprechend ist die umfassendere API, die der Dienst den Clients bietet, immer noch dieselbe. Es ist also wahrscheinlich, dass eine App den Großteil der Logik, die für die Implementierung von MediaBrowserService erforderlich ist, beibehalten und für die neue MediaLibraryService anpassen kann.

    Die Hauptunterschiede im Vergleich zur Legacy-MediaBrowserServiceCompat sind:

    • Lebenszyklusmethoden des Dienstes implementieren:Die Methoden, die im Dienst selbst überschrieben werden müssen, sind onCreate/onDestroy. Hier weist eine App die Bibliothekssitzung, den Player und andere Ressourcen zu oder gibt sie frei. Zusätzlich zu den standardmäßigen Lebenszyklusmethoden für Dienste muss eine App onGetSession(MediaSession.ControllerInfo) überschreiben, um die in onCreate erstellte MediaLibrarySession zurückzugeben.

    • MediaLibraryService.MediaLibrarySessionCallback implementieren:Zum Erstellen einer Sitzung ist ein MediaLibraryService.MediaLibrarySessionCallback erforderlich, der die eigentlichen Domain-API-Methoden implementiert. Anstatt API-Methoden des Legacy-Dienstes zu überschreiben, überschreiben Sie stattdessen die Methoden von MediaLibrarySession.Callback.

      Der Callback wird dann verwendet, um die MediaLibrarySession zu erstellen:

      Kotlin

      mediaLibrarySession = MediaLibrarySession.Builder(context, player, MySessionCallback()).build()

      Java

      mediaLibrarySession =
          new MediaLibrarySession.Builder(context, player, new MySessionCallback()).build();

      Die vollständige API von „MediaLibrarySessionCallback“ finden Sie in der API-Dokumentation.

    • MediaSession.Callback.onAddMediaItems() implementieren: Der Callback onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) dient verschiedenen aktuellen und älteren API-Methoden, mit denen Mediendateien abwärtskompatibel für die Wiedergabe im Player hinzugefügt werden. Dazu gehören die MediaController.set/addMediaItems()-Methoden des Media3-Controllers sowie die TransportControls.prepareFrom*/playFrom*-Methoden der Legacy-API. Eine Beispielimplementierung des Callbacks finden Sie in der PlaybackService der Session-Demo-App.

    • AndroidX Media3 verwendet androidx.media3.common.MediaItem anstelle von MediaBrowserCompat.MediaItem und MediaMetadataCompat. Teile deines Codes, die mit den Legacy-Klassen verknüpft sind, müssen entsprechend geändert oder stattdessen der Media3-MediaItem zugeordnet werden.

    • Das allgemeine asynchrone Programmiermodell wurde in Futures geändert, im Gegensatz zum trennbaren Result-Ansatz von MediaBrowserServiceCompat. Ihre Dienstimplementierung kann ein asynchrones ListenableFuture zurückgeben, anstatt ein Ergebnis zu trennen, oder ein sofortiges Future zurückgeben, um einen Wert direkt zurückzugeben.

PlayerNotificationManager entfernen

Der MediaLibraryService unterstützt Medienbenachrichtigungen automatisch und der PlayerNotificationManager kann entfernt werden, wenn ein MediaLibraryService oder MediaSessionService verwendet wird.

Eine App kann die Benachrichtigung anpassen, indem sie in onCreate() ein benutzerdefiniertes MediaNotification.Provider festlegt, das das DefaultMediaNotificationProvider ersetzt. Der MediaLibraryService startet den Dienst dann nach Bedarf im Vordergrund.

Durch das Überschreiben von MediaLibraryService.updateNotification() kann eine App außerdem die vollständige Kontrolle über das Posten einer Benachrichtigung und das Starten/Beenden des Dienstes im Vordergrund übernehmen.

Clientcode mit einem MediaBrowser migrieren

Mit AndroidX Media3 implementiert ein MediaBrowser die MediaController/Player-Schnittstellen und kann neben dem Durchsuchen der Mediathek auch zur Steuerung der Medienwiedergabe verwendet werden. Wenn Sie in der Legacy-Welt einen MediaBrowserCompat und einen MediaControllerCompat erstellen mussten, können Sie dasselbe tun, indem Sie nur den MediaBrowser in Media3 verwenden.

Sie können einen MediaBrowser erstellen und warten, bis die Verbindung zum Dienst hergestellt ist:

Kotlin

scope.launch {
  val sessionToken = SessionToken(context, ComponentName(context, "MusicService"))
  browser =
    MediaBrowser.Builder(context, sessionToken)
      .setListener(BrowserListener())
      .buildAsync()
      .await()
}

Java

SessionToken sessionToken =
    new SessionToken(context, new ComponentName(context, "MusicService"));
ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken)
        .setListener(new BrowserListener())
        .buildAsync();

Unter Wiedergabe in der Mediensitzung steuern erfährst du, wie du einen MediaController erstellst, um die Wiedergabe im Hintergrund zu steuern.

Weitere Schritte und Bereinigung

Fehler bei instabiler API

Nach der Migration zu Media3 werden möglicherweise Lint-Fehler zu instabilen API-Nutzungen angezeigt. Diese APIs können sicher verwendet werden. Die Lint-Fehler sind ein Nebenprodukt unserer neuen Garantien für die Binärkompatibilität. Wenn Sie keine strikte Binärkompatibilität benötigen, können diese Fehler mit einer @OptIn-Annotation sicher unterdrückt werden.

Hintergrund

Weder ExoPlayer v1 noch v2 boten strenge Garantien für die Binärkompatibilität der Bibliothek zwischen nachfolgenden Versionen. Die ExoPlayer API-Oberfläche ist absichtlich sehr groß, damit Apps fast jeden Aspekt der Wiedergabe anpassen können. In nachfolgenden Versionen von ExoPlayer wurden gelegentlich Symbolumbenennungen oder andere funktionsgefährdende Änderungen eingeführt (z.B. neue erforderliche Methoden für Schnittstellen). In den meisten Fällen wurden diese Probleme behoben, indem das neue Symbol eingeführt und das alte Symbol für einige Versionen eingestellt wurde, damit Entwickler Zeit hatten, ihre Verwendungen zu migrieren. Das war jedoch nicht immer möglich.

Diese funktionsgefährdenden Änderungen führten zu zwei Problemen für Nutzer der ExoPlayer-Bibliotheken v1 und v2:

  1. Ein Upgrade auf die ExoPlayer-Version kann dazu führen, dass Code nicht mehr kompiliert wird.
  2. Bei einer App, die sowohl direkt als auch über eine Zwischenbibliothek von ExoPlayer abhängig war, musste sichergestellt werden, dass beide Abhängigkeiten dieselbe Version hatten. Andernfalls konnten binäre Inkompatibilitäten zu Abstürzen während der Laufzeit führen.

Verbesserungen in Media3

Media3 garantiert die Binärkompatibilität für einen Teil der API-Oberfläche. Die Teile, die keine Binärkompatibilität garantieren, sind mit @UnstableApi gekennzeichnet. Um diese Unterscheidung deutlich zu machen, wird bei der Verwendung instabiler API-Symbole ein Lint-Fehler generiert, sofern sie nicht mit @OptIn annotiert sind.

Nach der Migration von ExoPlayer v2 zu Media3 werden möglicherweise viele Lint-Fehler für instabile APIs angezeigt. Dadurch kann der Eindruck entstehen, dass Media3 weniger stabil ist als ExoPlayer v2. Das ist jedoch nicht der Fall. Die „instabilen“ Teile der Media3 API sind genauso stabil wie die gesamte ExoPlayer v2 API-Oberfläche. Die Garantien der stabilen Media3 API-Oberfläche sind in ExoPlayer v2 überhaupt nicht verfügbar. Der Unterschied besteht lediglich darin, dass Sie jetzt durch einen Lint-Fehler auf die verschiedenen Stabilitätsstufen aufmerksam gemacht werden.

Fehler bei instabilen API-Lint-Fehlern beheben

Weitere Informationen zum Annotieren von Java- und Kotlin-Nutzungen instabiler APIs mit @OptIn finden Sie im Abschnitt zur Fehlerbehebung bei diesen Lint-Fehlern.

Verworfene APIs

In Android Studio sind Aufrufe von verworfenen APIs durchgestrichen. Wir empfehlen, solche Aufrufe durch die entsprechende Alternative zu ersetzen. Bewegen Sie den Mauszeiger auf das Symbol, um das JavaDoc aufzurufen, in dem die zu verwendende API angegeben ist.

Screenshot: JavaDoc mit Alternative für verworfene Methode anzeigen
Abbildung 3: JavaDoc-Kurzinfo in Android Studio, die eine Alternative für ein verworfenes Symbol vorschlägt

Codebeispiele und Demo-Apps

  • AndroidX Media3-Sitzungs-Demo-App (Mobilgeräte und Wear OS)
    • Benutzerdefinierte Aktionen
    • System-UI-Benachrichtigung, MediaButton/BT
    • Wiedergabesteuerung mit Google Assistant
  • UAMP: Android Media Player (Zweig media3) (Mobilgeräte, AutomotiveOS)
    • System-UI-Benachrichtigung, MediaButton/BT, Fortsetzung der Wiedergabe
    • Wiedergabesteuerung über Google Assistant/Wear OS
    • AutomotiveOS: benutzerdefinierter Befehl und Anmeldung