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

Bevor Sie migrieren, lesen Sie die folgenden Abschnitte, 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.
  • Mit MediaBrowser/MediaController auf die Player API über Komponenten/Prozesse hinweg zugreifen.
  • Die erweiterten Funktionen der MediaSession und MediaController API verwenden.
  • Wiedergabefunktionen mit detaillierter Zugriffssteuerung bewerben.
  • App vereinfachen , indem Sie MediaSessionConnector und PlayerNotificationManager entfernen.
  • Abwärtskompatibel mit Media-Compat-Client-APIs (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

Media APIs zu AndroidX Media3 migrieren

  • ExoPlayer und seine Erweiterungen
    Dazu gehören alle Module des alten ExoPlayer-Projekts mit Ausnahme des eingestellten Moduls „mediasession“. 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+)
    Entfernen Sie MediaSessionConnector und verwenden Sie stattdient androidx.media3.session.MediaSession.
  • MediaBrowserServiceCompat (abhängig von den androidx.media.* Paketen von androidx.media:media:1.4.3+)
    Migrieren Sie 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+)
    Migrieren Sie Clientcode, der MediaBrowserCompat oder MediaControllerCompat verwendet, zu androidx.media3.session.MediaBrowser mit androidx.media3.common.MediaItem.

Vorbereitung

  1. Achten Sie darauf, dass Ihr Projekt unter Quellcodeverwaltung steht.

    So können Sie Änderungen, die von skriptbasierten Migrationstools vorgenommen wurden, ganz einfach rückgängig machen. Wenn Ihr Projekt noch nicht unter Quellcodeverwaltung steht, ist jetzt ein guter Zeitpunkt, damit zu beginnen. Wenn Sie das aus irgendeinem Grund nicht möchten, erstellen Sie vor Beginn der Migration eine Sicherungskopie Ihres Projekts.

  2. App aktualisieren

    • Wir empfehlen, Ihr Projekt auf die neueste Version der ExoPlayer-Bibliothek zu aktualisieren und alle Aufrufe veralteter Methoden zu entfernen. Wenn Sie das Skript für die Migration verwenden möchten, muss die Version, auf die Sie aktualisieren, mit der Version übereinstimmen, die vom Skript verarbeitet wird.

    • 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 aktualisierten Abhängigkeiten von oben funktioniert. Beispiel:

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

    • Migrieren Sie von com.google.android.exoplayer2.PlayerView zu com.google.android.exoplayer2.StyledPlayerView. Das ist erforderlich, da es in AndroidX Media3 kein Äquivalent zu com.google.android.exoplayer2.PlayerView gibt.

ExoPlayer mit Skriptunterstützung migrieren

Das Skript erleichtert den Wechsel von com.google.android.exoplayer2 zur neuen Paket- und Modulstruktur unter androidx.media3. Das Skript 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 Gruppe der Dateien aufzulisten, die für die Migration ausgewählt wurden. Verwenden Sie -f, um die Auflistung ohne Warnungen zu erzwingen:

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

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

    Wenn das Skript 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 diese 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. Melden Sie einen Fehler, wenn Sie der Meinung sind, dass das Skript ein allgemeines Problem hat, das ohne die Option -f eingeführt wurde.
  2. Erstellen Sie das Projekt: Verwenden Sie entweder ./gradlew clean build oder wählen Sie in Android Studio Datei > Projekt mit Gradle-Dateien synchronisieren und dann Erstellen > Projekt bereinigen und dann Erstellen > Projekt neu erstellen aus. Beobachten Sie den Build auf dem Tab „Build – Build-Ausgabe“ von Android Studio.

Empfohlene Folgeschritte:

  1. Opt-in für Fehler bezüglich der Verwendung instabiler APIs beheben.
  2. Veraltete API-Aufrufe ersetzen: Verwenden Sie die vorgeschlagene Ersatz-API. Bewegen Sie den Mauszeiger in Android Studio auf die Warnung und lesen Sie die JavaDoc des veralteten Symbols, um herauszufinden, was Sie anstelle eines bestimmten Aufrufs verwenden können.
  3. Importanweisungen sortieren: Öffnen Sie das Projekt in Android Studio, klicken Sie dann im Projekt-Viewer mit der rechten Maustaste auf einen Paketordnerknoten und wählen Sie für die Pakete, die die geänderten Quelldateien enthalten, Importe optimieren aus.

MediaSessionConnector durch androidx.media3.session.MediaSession ersetzen

In der alten MediaSessionCompat-Welt war MediaSessionConnector für die Synchronisierung des Player-Status mit dem Status der Sitzung und für den Empfang von Befehlen von Controllern zuständig, die an geeignete Player-Methoden delegiert werden mussten. Mit AndroidX Media3 wird dies direkt von MediaSession ohne Connector erledigt.

  1. Alle Verweise und Verwendungen von MediaSessionConnector entfernen:Wenn Sie das automatisierte Skript verwendet haben, um ExoPlayer-Klassen und -Pakete zu migrieren, hat das Skript Ihren Code wahrscheinlich in einem nicht kompilierbaren Zustand bezüglich MediaSessionConnector belassen, der nicht behoben werden kann. Android Studio zeigt Ihnen den fehlerhaften Code an, 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.10.0"
    
  3. Ersetzen Sie MediaSessionCompat durch androidx.media3.session.MediaSession.

  4. Verwenden Sie an der Codestelle, an der Sie die alte MediaSessionCompat erstellt haben, androidx.media3.session.MediaSession.Builder um eine MediaSession zu erstellen. Übergeben Sie den Player , um den Sitzungs-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 nach Bedarf für Ihre App. Das ist optional. Wenn Sie Controllern erlauben möchten, dem Player Media-Elemente hinzuzufügen, implementieren Sie MediaSession.Callback.onAddMediaItems(). Es stellt verschiedene aktuelle und alte API-Methoden bereit, mit denen Mediendateien auf abwärtskompatible Weise dem Player zur Wiedergabe hinzugefügt werden. Dazu gehören die Methoden MediaController.set/addMediaItems() des Media3-Controllers sowie die Methoden TransportControls.prepareFrom*/playFrom* der alten API. Eine Beispielimplementierung von onAddMediaItems finden Sie in der PlaybackService der Sitzungs-Demo-App.

  6. Geben Sie die Media-Session an der Codestelle frei, an der Sie Ihre Sitzung vor der Migration beendet haben:

    Kotlin

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

    Java

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

MediaSessionConnector-Funktionalität 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. Die JavaDoc von MediaLibraryService und seiner Superklasse MediaSessionService bietet eine gute Einführung in die API und das asynchrone Programmiermodell des Dienstes.

MediaLibraryService ist abwärtskompatibel mit MediaBrowserService. Eine Client-App, die MediaBrowserCompat oder MediaControllerCompat verwendet, funktioniert weiterhin ohne Codeänderungen, wenn sie eine Verbindung zu MediaLibraryService herstellt. Für einen Client ist es transparent, ob Ihre App MediaLibraryService oder das alte MediaBrowserServiceCompat verwendet.

Diagramm der App-Komponenten mit Dienst, Aktivität und externen Apps.
Abbildung 1: Übersicht über die Komponenten der Media-App
  1. Damit die Abwärtskompatibilität funktioniert, müssen Sie beide Dienstschnittstellen in der AndroidManifest.xml bei Ihrem Dienst 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.10.0"
    
  3. Ändern Sie Ihren Dienst so, dass er von MediaLibraryService anstelle von MediaBrowserService erbt. Wie bereits erwähnt, ist MediaLibraryService mit dem alten MediaBrowserService kompatibel. Dementsprechend ist die breitere API, die der Dienst Clients anbietet, immer noch dieselbe. Es ist also wahrscheinlich, dass eine App die meiste Logik beibehalten kann, die zur Implementierung von MediaBrowserService erforderlich ist, und sie für das neue MediaLibraryService anpassen kann.

    Die Hauptunterschiede im Vergleich zu MediaBrowserServiceCompat sind folgende:

    • Implementieren Sie die Methoden des Dienstlebenszyklus: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 bzw. gibt sie frei. Zusätzlich zu den Standardmethoden des Dienstlebenszyklus muss eine App onGetSession(MediaSession.ControllerInfo) überschreiben, um die MediaLibrarySession zurückzugeben, die in onCreate erstellt wurde.

    • Implementieren Sie MediaLibraryService.MediaLibrarySessionCallback: Zum Erstellen einer Sitzung ist ein MediaLibraryService.MediaLibrarySessionCallback erforderlich, das die tatsächlichen Domain-API-Methoden implementiert. Anstatt also API-Methoden des alten 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.

    • Implementieren Sie MediaSession.Callback.onAddMediaItems(): Der Callback onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) stellt verschiedene aktuelle und alte API-Methoden bereit, mit denen Mediendateien auf abwärtskompatible Weise dem Player zur Wiedergabe hinzugefügt werden. Dazu gehören die Methoden MediaController.set/addMediaItems() des Media3-Controllers sowie die Methoden TransportControls.prepareFrom*/playFrom* der alten API. Eine Beispielimplementierung des Callbacks kann gefunden werden in der PlaybackService der Sitzungs-Demo-App.

    • AndroidX Media3 verwendet androidx.media3.common.MediaItem anstelle von MediaBrowserCompat.MediaItem und MediaMetadataCompat. Teile Ihres Codes, die an die alten Klassen gebunden sind, müssen entsprechend geändert oder stattdessen auf das Media3-MediaItem abgebildet werden.

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

PlayerNotificationManager entfernen

MediaLibraryService unterstützt automatisch Media-Benachrichtigungen und PlayerNotificationManager kann entfernt werden, wenn Sie MediaLibraryService oder MediaSessionService verwenden.

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

Durch Überschreiben von MediaLibraryService.updateNotification() kann eine App 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 Schnittstellen MediaController/Player und kann neben dem Durchsuchen der Media-Bibliothek auch zur Steuerung der Medienwiedergabe verwendet werden. Wenn Sie in der alten Welt ein MediaBrowserCompat und ein MediaControllerCompat erstellen mussten, können Sie dasselbe tun, indem Sie in Media3 nur das MediaBrowser verwenden.

Ein MediaBrowser kann erstellt werden und auf die Herstellung der Verbindung zum Dienst warten:

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 Media-Session steuern erfahren Sie, wie Sie einen MediaController erstellen, um die Wiedergabe im Hintergrund zu steuern.

Weitere Schritte und Bereinigung

Fehler bei instabilen APIs

Nach der Migration zu Media3 werden möglicherweise Lint-Fehler bezüglich der Verwendung instabiler APIs angezeigt. Diese APIs können sicher verwendet werden und die Lint-Fehler sind ein Nebenprodukt unserer neuen Garantien für die binäre Kompatibilität. Wenn Sie keine strenge binäre Kompatibilität benötigen, können diese Fehler mit der Annotation @OptIn sicher unterdrückt werden.

Hintergrund

Weder ExoPlayer v1 noch v2 boten strenge Garantien für die binäre Kompatibilität der Bibliothek zwischen nachfolgenden Versionen. Die ExoPlayer API-Oberfläche ist von Natur aus sehr groß, damit Apps fast jeden Aspekt der Wiedergabe anpassen können. In nachfolgenden Versionen von ExoPlayer wurden gelegentlich Symbole umbenannt oder andere Breaking Changes eingeführt (z.B. neue erforderliche Methoden für Schnittstellen). In den meisten Fällen wurden diese Breaking Changes dadurch gemildert, dass das neue Symbol eingeführt und das alte Symbol für einige Versionen als veraltet markiert wurde, damit Entwickler Zeit hatten, ihre Verwendungen zu migrieren. Das war jedoch nicht immer möglich.

Diese Breaking Changes führten zu zwei Problemen für Nutzer der ExoPlayer v1- und v2-Bibliotheken:

  1. Ein Upgrade von auf die ExoPlayer-Version konnte dazu führen, dass der Code nicht mehr kompiliert wurde.
  2. Eine App, die sowohl direkt als auch über eine Zwischenbibliothek von ExoPlayer abhing, musste dafür sorgen, dass beide Abhängigkeiten dieselbe Version hatten. Andernfalls konnten binäre Inkompatibilitäten zu Laufzeitabstürzen führen.

Verbesserungen in Media3

Media3 garantiert die binäre Kompatibilität für eine Teilmenge der API-Oberfläche. Die Teile, für die keine binäre Kompatibilität garantiert wird, sind mit @UnstableApi gekennzeichnet. Um diese Unterscheidung deutlich zu machen, wird bei der Verwendung instabiler API-Symbole ein Lint-Fehler generiert, es sei denn, sie sind mit @OptIn annotiert.

Nach der Migration von ExoPlayer v2 zu Media3 werden möglicherweise viele Lint-Fehler für instabile APIs angezeigt. Dadurch kann es so aussehen, als wäre Media3 weniger stabil als ExoPlayer v2. Das stimmt nicht. Die „instabilen“ Teile der Media3 API haben die gleiche Stabilität 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 unterschiedlichen Stabilitätsstufen aufmerksam gemacht werden.

Lint-Fehler für instabile APIs behandeln

Siehe den Abschnitt zur Fehlerbehebung zu diesen Lint-Fehlern für Details dazu, wie Sie Java- und Kotlin-Verwendungen instabiler APIs mit @OptIn annotieren.

Veraltete APIs

Möglicherweise werden Aufrufe veralteter APIs in Android Studio durchgestrichen. Wir empfehlen, solche Aufrufe durch die entsprechende Alternative zu ersetzen. Bewegen Sie den Mauszeiger auf das Symbol, um die JavaDoc aufzurufen, in der die stattdessen zu verwendende API angegeben ist.

Screenshot: So wird JavaDoc mit einer Alternative zur eingestellten Methode angezeigt
Abbildung 3: Im JavaDoc-Tooltip in Android Studio wird eine Alternative für jedes veraltete Symbol vorgeschlagen.

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 Mediaplayer (Branch media3) (Mobilgeräte, AutomotiveOS)
    • System-UI-Benachrichtigung, MediaButton/BT, Wiedergabe fortsetzen
    • Wiedergabesteuerung mit Google Assistant/Wear OS
    • AutomotiveOS: benutzerdefinierter Befehl und Anmeldung