Migrationsanleitung für AndroidX Media3

Anwendungen, die derzeit die eigenständige Bibliothek com.google.android.exoplayer2 und androidx.media verwenden, sollten zu androidx.media3 migriert werden. Mit dem Migrationsskript können Sie Gradle-Build-Dateien, Java- und Kotlin-Quelldateien sowie XML-Layoutdateien von ExoPlayer 2.19.1 zu AndroidX Media3 1.1.1 migrieren.

Übersicht

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

Gründe für die Migration zu Jetpack Media3

  • Dies ist das neue Zuhause von ExoPlayer. com.google.android.exoplayer2 wurde jedoch eingestellt.
  • Greifen Sie mit MediaBrowser/MediaController auf die Player API für verschiedene Komponenten/Prozesse zu.
  • Sie können die erweiterten Funktionen der MediaSession und der MediaController API verwenden.
  • Werben Sie mit der detaillierten Zugriffssteuerung für Wiedergabefunktionen.
  • Vereinfache deine App, indem du MediaSessionConnector und PlayerNotificationManager entfernst.
  • Abwärtskompatibel mit medienkompatiblen Client-APIs (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

Zu AndroidX Media3 zu migrierende Medien-APIs

  • ExoPlayer und zugehörige Erweiterungen
    Dazu gehören alle Module des alten ExoPlayer-Projekts mit Ausnahme des nicht mehr unterstützten mediasession-Moduls. Anwendungen oder Module, die von den 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 MediaSessionConnector und verwende stattdessen das androidx.media3.session.MediaSession.
  • MediaBrowserServiceCompat (abhängig von den androidx.media.*-Paketen von androidx.media:media:1.4.3+)
    Migrieren Sie abgeleitete Klassen von androidx.media.MediaBrowserServiceCompat zu androidx.media3.session.MediaLibraryService und programmieren Sie mit MediaBrowserCompat.MediaItem zu androidx.media3.common.MediaItem.
  • MediaBrowserCompat (abhängig von den android.support.v4.media.*-Paketen von androidx.media:media:1.4.3+)
    Migrieren Sie den Clientcode mit MediaBrowserCompat oder MediaControllerCompat, um androidx.media3.session.MediaBrowser mit androidx.media3.common.MediaItem zu verwenden.

Voraussetzungen

  1. Projekt mit Versionsverwaltung prüfen

    Sorgen Sie dafür, dass sich von skriptbasierten Migrationstools übernommene Änderungen einfach rückgängig machen lassen. Wenn Ihr Projekt noch nicht der Versionsverwaltung unterliegt, ist jetzt ein guter Zeitpunkt, damit zu beginnen. Wenn Sie dies aus irgendeinem Grund nicht tun möchten, erstellen Sie eine Sicherungskopie des 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 für die Migration das Skript verwenden möchten, müssen Sie die Version, auf die Sie aktualisieren, mit der Version abstimmen, die vom Skript verarbeitet wird.

    • Erhöhen Sie den Wert für compileSdkVersion Ihrer App auf mindestens 32.

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

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

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

ExoPlayer mit Skriptunterstützung migrieren

Das Skript erleichtert das Verschieben von com.google.android.exoplayer2 zur neuen Paket- und Modulstruktur unter androidx.media3. Das Skript wendet einige Validierungsprüfungen auf Ihr Projekt an und gibt Warnungen aus, wenn die Validierung fehlschlägt. Andernfalls werden die Zuordnungen von umbenannten Klassen und Paketen in den Ressourcen eines Android-Gradle-Projekts in Java oder Kotlin angewendet.

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ühren Sie das Skript mit -m aus, um Media3 Pakete, Klassen und Module zuzuordnen. Wenn Sie das Skript mit der Option -m ausführen, werden Änderungen auf die ausgewählten Dateien angewendet.

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

    Wenn das Skript einen Verstoß gegen die Voraussetzungen feststellt, 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, wie sich das Skript Ihren Code geändert hat: Verwenden Sie ein diff-Tool und beheben Sie potenzielle Probleme. Wenn Sie der Meinung sind, dass im Skript ein allgemeines Problem vorliegt, das aufgetreten ist, ohne die Option -f zu übergeben, können Sie einen Fehler melden.
  2. Projekt erstellen: Verwende ./gradlew clean build oder wähle in Android Studio Datei > Projekt mit Gradle-Dateien synchronisieren > Build > Projekt bereinigen und dann Build > Projekt neu erstellen aus. Überwache deinen Build in Android Studio auf dem Tab Build – Build Output.

Empfohlene weitere Schritte:

  1. Beheben Sie die Zustimmung für Fehler bei der Verwendung instabiler APIs.
  2. Verworfene API-Aufrufe ersetzen: Verwenden Sie die vorgeschlagene Ersatz-API. Halten Sie den Mauszeiger über die Warnung in Android Studio und konsultieren Sie das JavaDoc zum veralteten Symbol, um herauszufinden, was anstelle eines bestimmten Aufrufs verwendet werden sollte.
  3. Importanweisungen sortieren: Öffnen Sie das Projekt in Android Studio, klicken Sie dann mit der rechten Maustaste in der Projektansicht 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 Legacy-MediaSessionCompat-Umgebung war MediaSessionConnector dafür zuständig, den Status des Spielers mit dem Status der Sitzung zu synchronisieren und Befehle von Controllern zu empfangen, die Delegierung an geeignete Spielermethoden mussten. Bei AndroidX Media3 erfolgt dies direkt von MediaSession, ohne dass ein Connector erforderlich ist.

  1. Entfernen Sie alle Verweise auf und die Verwendung von MediaSessionConnector: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 belassen, der die MediaSessionConnector betrifft, die nicht aufgelöst werden kann. 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.3.1"
    
  3. Ersetzen Sie MediaSessionCompat durch androidx.media3.session.MediaSession.

  4. Verwenden Sie auf der Codewebsite, auf der Sie den Legacy-MediaSessionCompat erstellt haben, androidx.media3.session.MediaSession.Builder, um eine MediaSession zu erstellen. Übergeben Sie den Player, um den Session Builder zu erstellen.

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player)
        .setSessionCallback(MySessionCallback())
        .build()
    
  5. Implementieren Sie MySessionCallback gemäß den Anforderungen Ihrer App. Dies ist optional. Wenn du Controllern erlauben möchtest, dem Player Medienelemente hinzuzufügen, implementiere MediaSession.Callback.onAddMediaItems(). Sie unterstützt verschiedene aktuelle und Legacy-API-Methoden, mit denen dem Player Medienelemente für die abwärtskompatible Wiedergabe hinzugefügt werden. Dazu gehören die MediaController.set/addMediaItems()-Methoden des Media3-Controllers und die TransportControls.prepareFrom*/playFrom*-Methoden der Legacy-API. Eine Beispielimplementierung von onAddMediaItems findest du in der PlaybackService der Sitzungsdemo-App.

  6. Geben Sie die Mediensitzung an der Codewebsite frei, an der Sie die Sitzung vor der Migration gelöscht haben:

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

MediaSessionConnector-Funktion in Media3

Die folgende Tabelle zeigt die Media3 APIs, über die Funktionen verarbeitet werden, die zuvor in MediaSessionConnector implementiert wurden.

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

MediaBrowserService zu MediaLibraryService migrieren

Mit AndroidX Media3 wird MediaLibraryService eingeführt, das MediaBrowserServiceCompat ersetzt. Das JavaDoc von MediaLibraryService und die zugehörige übergeordnete Klasse MediaSessionService bieten 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 einer MediaLibraryService hergestellt wird. Bei einem Client ist es transparent, ob die Anwendung eine MediaLibraryService- oder eine Legacy-MediaBrowserServiceCompat verwendet.

Diagramm der App-Komponenten mit Dienst, Aktivität und externen Apps
Abbildung 1: Übersicht über Komponenten einer Medien-App
  1. Damit die Abwärtskompatibilität funktioniert, müssen Sie beide Dienstschnittstellen für Ihren 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.3.1"
    
  3. Ändern Sie den Dienst so, dass er von einer MediaLibraryService anstelle von MediaBrowserService übernimmt. Wie bereits erwähnt, ist der MediaLibraryService mit dem Legacy-MediaBrowserService kompatibel. Entsprechend bleibt die weiter gefasste API, die der Dienst den Kunden anbietet, unverändert. Daher ist es wahrscheinlich, dass eine Anwendung den Großteil der für die Implementierung von MediaBrowserService erforderlichen Logik beibehalten und an die neue MediaLibraryService anpassen kann.

    Die Hauptunterschiede zum Legacy-MediaBrowserServiceCompat sind:

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

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

      Mit dem Callback wird dann das MediaLibrarySession erstellt:

      mediaLibrarySession =
            MediaLibrarySession.Builder(this, player, 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>) unterstützt verschiedene aktuelle und alte API-Methoden, mit denen dem Player Medienelemente für die abwärtskompatible Wiedergabe 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 findest du in der PlaybackService der Sitzungsdemo-App.

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

    • Das allgemeine asynchrone Programmiermodell wurde in Futures geändert, im Gegensatz zum Ansatz für trennbare Result 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.

PlayerBenachrichtigungManager entfernen

MediaLibraryService unterstützt Medienbenachrichtigungen automatisch und PlayerNotificationManager kann bei Verwendung von MediaLibraryService oder MediaSessionService entfernt werden.

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

Durch das Überschreiben von MediaLibraryService.updateNotification() kann eine Anwendung weiterhin die volle Verantwortung für das Posten einer Benachrichtigung übernehmen und den Dienst nach Bedarf im Vordergrund starten/beenden.

Clientcode mit einem MediaBrowser migrieren

Bei AndroidX Media3 implementiert ein MediaBrowser die MediaController/Player-Schnittstellen und kann nicht nur zum Durchsuchen der Medienbibliothek, sondern auch zur Steuerung der Medienwiedergabe verwendet werden. Wenn Sie ein MediaBrowserCompat und ein MediaControllerCompat in der Legacy-Umgebung erstellen mussten, können Sie dies ebenfalls tun, indem Sie nur die MediaBrowser in Media3 verwenden.

Ein MediaBrowser kann erstellt werden und wartet darauf, dass die Verbindung zum Dienst hergestellt wurde:

scope.launch {
    val sessionToken =
        SessionToken(context, ComponentName(context, MusicService::class.java)
    browser =
        MediaBrowser.Builder(context, sessionToken))
            .setListener(BrowserListener())
            .buildAsync()
            .await()
    // Get the library root to start browsing the library.
    root = browser.getLibraryRoot(/* params= */ null).await();
    // Add a MediaController.Listener to listen to player state events.
    browser.addListener(playerListener)
    playerView.setPlayer(browser)
}

Informationen zum Erstellen einer MediaController zur Steuerung der Wiedergabe im Hintergrund finden Sie unter Wiedergabe in der Mediensitzung steuern.

Weitere Schritte und Bereinigung

Instabile API-Fehler

Nach der Migration zu Media3 können Lint-Fehler aufgrund instabiler API-Nutzung auftreten. Die Verwendung dieser APIs ist sicher und die Lint-Fehler sind Nebenprodukt unserer neuen Binärkompatibilitätsgarantien. Wenn Sie keine strenge Binärkompatibilität benötigen, können diese Fehler mit einer @OptIn-Annotation sicher unterdrückt werden.

Hintergrund

Weder ExoPlayer v1 noch v2 bietet strenge Garantien für die Binärkompatibilität der Bibliothek zwischen nachfolgenden Versionen. Die ExoPlayer API-Oberfläche ist von Grund auf sehr groß, damit Apps nahezu 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 Benutzeroberflächen. In den meisten Fällen wurden diese Fehler abgemildert, indem das neue Symbol zusammen mit dem alten Symbol für einige Versionen eingeführt wurde, um Entwicklern Zeit zu geben, ihre Nutzungen zu migrieren. Dies war jedoch nicht immer möglich.

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

  1. Ein Upgrade auf die ExoPlayer-Version kann dazu führen, dass der Code nicht mehr kompiliert wird.
  2. Eine Anwendung, die sowohl direkt als auch über eine Zwischenbibliothek von ExoPlayer abhängig war, musste sicherstellen, dass beide Abhängigkeiten dieselbe Version haben. Andernfalls konnten Inkompatibilitäten der Binärprogramme zu Laufzeitabstürzen führen.

Verbesserungen bei Media3

Media3 garantiert die Binärkompatibilität für einen Teil der API-Oberfläche. Die Teile, die die Binärkompatibilität nicht garantieren, sind mit @UnstableApi gekennzeichnet. Zur Klarstellung dieser Unterscheidung erzeugen die Verwendung instabiler API-Symbole einen Lint-Fehler, sofern sie nicht mit @OptIn annotiert sind.

Nach der Migration von ExoPlayer v2 zu Media3 können viele instabile API-Lint-Fehler auftreten. Dies kann den Anschein erwecken, als wäre Media3 „weniger stabil“ als ExoPlayer v2. Das ist nicht der Fall. Die „instabilen“ Teile der Media3 API haben dieselbe Stabilität wie die gesamte Oberfläche der ExoPlayer v2 API. Die Garantien für eine stabile Media3 API-Oberfläche sind in ExoPlayer v2 nicht verfügbar. Der Unterschied besteht darin, dass Sie jetzt durch einen Lint-Fehler auf die verschiedenen Stabilitätsstufen hingewiesen werden.

Instabile API-Lint-Fehler beheben

Sie haben zwei Möglichkeiten, um die instabilen API-Lint-Fehler zu handhaben:

  • Verwenden Sie eine stabile API, mit der das gleiche Ergebnis erzielt wird.
  • Verwenden Sie weiterhin die instabile API und hat die Nutzung mit @OptIn annotiert.

    import androidx.annotation.OptIn
    import androidx.media3.common.util.UnstableApi
    
    @OptIn(UnstableApi::class)
    fun functionUsingUnstableApi() {
      // Do something useful.
    }
    

    Es gibt auch eine kotlin.OptIn-Annotation, die nicht verwendet werden sollte. Zu diesem Zweck ist es wichtig, die Annotation androidx.annotation.OptIn zu verwenden.

    Screenshot: Opt-in-Anmerkung hinzufügen
    Abbildung 2: Annotation „@androidx.annotations.OptIn“ mit Android Studio hinzufügen

Für ganze Pakete kann ein package-info.java hinzugefügt werden:

@OptIn(markerClass = UnstableApi.class)
package name.of.your.package;

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

Sie können ganze Projekte aktivieren, indem Sie den spezifischen Lint-Fehler in der lint.xml-Datei unterdrücken. Weitere Informationen finden Sie im JavaDoc zur UnstableApi-Annotation.

Eingestellte APIs

Möglicherweise stellen Sie fest, dass Aufrufe an eingestellte APIs in Android Studio durchgestrichen werden. Wir empfehlen, solche Aufrufe durch die entsprechende Alternative zu ersetzen. Bewegen Sie den Mauszeiger über das Symbol, um das JavaDoc anzuzeigen, das angibt, welche API stattdessen verwendet werden soll.

Screenshot: Anzeige von JavaDoc mit einer alternativen Methode der veralteten Methode
Abbildung 3: Die JavaDoc-Kurzinfo in Android Studio schlägt eine Alternative für eingestellte Symbole vor.

Codebeispiele und Demo-Apps

  • AndroidX Media3-Session-Demo-App (Mobilgerät und Wear OS)
    • Benutzerdefinierte Aktionen
    • System-UI-Benachrichtigung, MediaButton/BT
    • Wiedergabesteuerung mit Google Assistant
  • UAMP: Android Media Player (zweig media3) (mobile, AutomotiveOS)
    • System-UI-Benachrichtigung, MediaButton/BT, Wiederaufnahme der Wiedergabe
    • Wiedergabesteuerung für Google Assistant/Wear OS
    • AutomotiveOS: Benutzerdefinierter Befehl und Anmeldung