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. Mit dem Migrationsskript kannst du Gradle-Build-Dateien, Java- und Kotlin-Quelldateien und XML-Layoutdateien von ExoPlayer2.19.1 zu AndroidX Media31.1.1 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 muss.

Vorteile der Migration zu Jetpack Media3

  • Dort ist ExoPlayer jetzt zu finden. com.google.android.exoplayer2 wird eingestellt.
  • Mit MediaBrowser/MediaController auf die Player API zugreifen
  • Verwenden Sie die erweiterten Funktionen der MediaSession API und der MediaController API.
  • Sie können 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)

Medien-APIs, die zu AndroidX Media3 migriert werden müssen

  • ExoPlayer und seine Erweiterungen
    Hierzu zählen 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 (je nach androidx.media.*-Paketen von androidx.media:media:1.4.3+)
    Entfernen Sie die MediaSessionConnector und verwenden Sie stattdessen die androidx.media3.session.MediaSession.
  • MediaBrowserServiceCompat (je nach androidx.media.*-Paketen von androidx.media:media:1.4.3+)
    Migrieren Sie Unterklassen von androidx.media.MediaBrowserServiceCompat zu androidx.media3.session.MediaLibraryService und Code mit MediaBrowserCompat.MediaItem zu androidx.media3.common.MediaItem.
  • MediaBrowserCompat (je nach 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. Prüfen, ob Ihr Projekt der Versionsverwaltung unterliegt

    Achten Sie darauf, dass Sie Änderungen, die mithilfe von Migrationstools mit Scripts angewendet wurden, problemlos rückgängig machen können. Wenn Sie Ihr Projekt noch nicht der Versionskontrolle unterzogen haben, 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, dein Projekt so zu aktualisieren, dass die aktuellste Version der ExoPlayer-Bibliothek verwendet wird. Entferne außerdem alle Aufrufe an eingestellte Methoden. Wenn Sie das Script für die Migration verwenden möchten, muss die Version, auf die Sie aktualisieren, mit der Version übereinstimmen, die vom Script 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 oben funktioniert. Beispiele:

      • Android-Gradle-Plug-in, Version 7.1.0
      • Gradle-Version: 7.4
    • Ersetzen Sie alle Importanweisungen mit einem Stern (*) durch voll qualifizierte Importanweisungen: Löschen Sie die Importanweisungen mit einem Stern und importieren Sie die voll qualifizierten Anweisungen mit Android Studio (F2 - Alt/Eingabetaste, F2 - Alt/Eingabetaste usw.).

    • Von com.google.android.exoplayer2.PlayerView zu com.google.android.exoplayer2.StyledPlayerView migrieren Das ist notwendig, da 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 Skript führt einige Validierungsprüfungen für Ihr Projekt durch und gibt Warnungen aus, wenn die Validierung fehlschlägt. Andernfalls werden die Zuordnungen der umbenannten 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. Lade das Migrationsskript aus dem Tag des ExoPlayer-Projekts auf GitHub herunter, das der Version entspricht, auf die du deine App aktualisiert hast:

    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 Script mit --help aus, um Informationen zu den Optionen zu erhalten.

  4. Führen Sie das Script mit -l aus, um die für die Migration ausgewählten Dateien aufzulisten. Mit -f können Sie die Liste ohne Warnungen 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 Skript mit der Option -m ausführen, werden Änderungen auf die ausgewählten Dateien angewendet.

    • Bei Validierungsfehlern anhalten, 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 die folgenden manuellen Schritte aus, nachdem Sie das Script mit der Option -m ausgeführt haben:

  1. Prüfen, wie das Script Ihren Code geändert hat: Verwenden Sie ein Differenztool und beheben Sie potenzielle Probleme. Wenn Sie der Meinung sind, dass das Script ein allgemeines Problem aufweist, das ohne Übergabe der Option -f eingeführt wurde, können 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 in Android Studio auf dem Tab Build – Build-Ausgabe beobachten.

Empfohlene weitere Schritte:

  1. Beheben Sie Fehler beim Opt-in für die 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 sehen Sie in der JavaDoc des eingestellten Symbols nach, was Sie anstelle eines bestimmten Aufrufs verwenden können.
  3. Importanweisungen sortieren: Öffnen Sie das Projekt in Android Studio, klicken Sie in der Projektansicht mit der rechten Maustaste auf einen Paketordnerknoten und wählen Sie bei den Paketen, die die geänderten Quelldateien enthalten, Importe optimieren aus.

MediaSessionConnector durch androidx.media3.session.MediaSession ersetzen

In der alten MediaSessionCompat-Umgebung 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 die entsprechenden Playermethoden delegiert werden mussten. Bei AndroidX Media3 erfolgt dies direkt über die MediaSession, ohne dass ein Connector erforderlich ist.

  1. Entfernen Sie alle Verweise und Verwendungen von MediaSessionConnector:Wenn Sie die ExoPlayer-Klassen und ‑Pakete mit dem automatischen Script migriert haben, ist Ihr Code wahrscheinlich in einem nicht kompilierbaren Zustand, was die MediaSessionConnector betrifft, die nicht aufgelöst 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, dem AndroidX Media3-Sitzungsmodul eine Implementierungsabhängigkeit hinzu und entfernen Sie die alte Abhängigkeit:

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

  4. Verwenden Sie auf der Code-Website, auf der Sie die alte MediaSessionCompat erstellt haben, androidx.media3.session.MediaSession.Builder, um eine MediaSession zu erstellen. Übergebe 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 es Nutzern mit einem Controller erlauben möchtest, dem Player Medienelemente hinzuzufügen, implementiere MediaSession.Callback.onAddMediaItems(). Es werden verschiedene aktuelle und ältere API-Methoden bereitgestellt, mit denen dem Player Medienelemente zur Wiedergabe auf abwärtskompatible Weise 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 von onAddMediaItems finden Sie in der PlaybackService der Sitzungs-Demo-App.

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

    mediaSession?.run {
      player.release()
      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.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

In AndroidX Media3 wird MediaLibraryService eingeführt, das MediaBrowserServiceCompat ersetzt. Die JavaDoc-Dokumentation von MediaLibraryService und ihrer Superklasse MediaSessionService bietet eine gute Einführung in die API und das asynchrone Programmiermodell des Dienstes.

Der MediaLibraryService ist abwärtskompatibel mit dem MediaBrowserService. Eine Clientanwendung, die MediaBrowserCompat oder MediaControllerCompat verwendet, funktioniert weiterhin ohne Codeänderungen, wenn eine Verbindung zu einer MediaLibraryService hergestellt wird. Für einen Kunden ist klar, ob in Ihrer App eine MediaLibraryService oder eine alte MediaBrowserServiceCompat verwendet wird.

Diagramm mit App-Komponenten, Diensten, Aktivitäten und externen Apps
Abbildung 1: Übersicht über die Komponenten von Medien-Apps
  1. Damit die Abwärtskompatibilität funktioniert, müssen Sie beide Dienstoberflächen in der AndroidManifest.xml mit 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.4.1"
    
  3. Ändern Sie Ihren Dienst so, dass er von MediaLibraryService statt von MediaBrowserService abgeleitet wird. Wie bereits erwähnt, ist MediaLibraryService mit der bisherigen MediaBrowserService kompatibel. Die umfassendere API, die der Dienst den Kunden anbietet, bleibt daher unverändert. Daher ist es wahrscheinlich, dass die meisten Logikfunktionen, die für die Implementierung der MediaBrowserService erforderlich sind, in einer App beibehalten und an die neue MediaLibraryService angepasst werden können.

    Die wichtigsten Unterschiede im Vergleich zur bisherigen MediaBrowserServiceCompat sind:

    • Implementiere die Methoden für den Dienstlebenszyklus: Die Methoden, die im Dienst selbst überschrieben werden müssen, sind onCreate/onDestroy, bei denen eine App die Bibliothekssitzung, den Player und andere Ressourcen zuweist/freigibt. Zusätzlich zu den standardmäßigen Methoden für den Dienstlebenszyklus muss eine App onGetSession(MediaSession.ControllerInfo) überschreiben, um die MediaLibrarySession zurückzugeben, die in onCreate erstellt wurde.

    • MediaLibraryService.MediaLibrarySessionCallback implementieren:Für die Erstellung einer Sitzung ist ein MediaLibraryService.MediaLibrarySessionCallback erforderlich, in dem die tatsächlichen Domain-API-Methoden implementiert sind. Statt die API-Methoden des alten Dienstes zu überschreiben, werden die Methoden des MediaLibrarySession.Callback überschrieben.

      Der Callback wird dann zum Erstellen des MediaLibrarySession verwendet:

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

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

    • Implementiere MediaSession.Callback.onAddMediaItems(): Der Callback onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) dient verschiedenen aktuellen und älteren API-Methoden, mit denen dem Player Medienelemente zur abwärtskompatiblen 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 finden Sie in der PlaybackService der Demo-App für die Sitzung.

    • In AndroidX Media3 wird androidx.media3.common.MediaItem anstelle von MediaBrowserCompat.MediaItem und MediaMetadataCompat verwendet. Teile deines Codes, die mit den alten 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 abnehmbaren Result-Ansatz der MediaBrowserServiceCompat. Ihre Dienstimplementierung kann ein asynchrones ListenableFuture zurückgeben, anstatt ein Ergebnis zu trennen, oder ein sofortiges Future zurückgeben, um direkt einen Wert zurückzugeben.

PlayerNotificationManager entfernen

Die MediaLibraryService unterstützt automatisch Medienbenachrichtigungen und die PlayerNotificationManager kann entfernt werden, wenn eine MediaLibraryService oder MediaSessionService verwendet wird.

Eine App kann die Benachrichtigung anpassen, indem sie in onCreate() eine benutzerdefinierte MediaNotification.Provider festlegt, die die DefaultMediaNotificationProvider ersetzt. Der MediaLibraryService sorgt dann dafür, dass der Dienst bei Bedarf im Vordergrund gestartet wird.

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

Clientcode mit einem MediaBrowser migrieren

Bei AndroidX Media3 implementiert ein MediaBrowser die MediaController/Player-Schnittstellen und kann neben dem Durchsuchen der Medienbibliothek auch zur Steuerung der Medienwiedergabe verwendet werden. Wenn du in der bisherigen Version eine MediaBrowserCompat und eine MediaControllerCompat erstellen musstest, kannst du das auch in Media3 tun, indem du nur die MediaBrowser verwendest.

Eine MediaBrowser kann erstellt und auf die Herstellung der Verbindung zum Dienst gewartet werden:

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)
}

Unter Wiedergabe in der Mediensitzung steuern erfährst du, wie du eine MediaController zum Steuern der Wiedergabe im Hintergrund erstellst.

Weitere Schritte und Bereinigung

Unstable API errors

Nach der Migration zu Media3 werden möglicherweise Lint-Fehler zu instabiler API-Nutzung angezeigt. Die APIs sind sicher und die Lint-Fehler sind ein Nebenprodukt unserer neuen Garantien für die Binärkompatibilität. Wenn Sie keine strenge Binärkompatibilität benötigen, können diese Fehler mit einer @OptIn-Anmerkung problemlos unterdrückt werden.

Hintergrund

Weder ExoPlayer v1 noch v2 boten strikte Garantien hinsichtlich der Binärkompatibilität der Bibliothek zwischen nachfolgenden Versionen. Die ExoPlayer API ist von Natur aus sehr umfangreich, 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 konnten diese Unterbrechungen durch die Einführung des neuen Symbols und die Einstellung des alten Symbols für einige Versionen gemildert werden, um Entwicklern Zeit für die Migration ihrer Verwendungen zu geben. Dies war jedoch nicht immer möglich.

Diese bahnbrechenden Änderungen führten zu zwei Problemen für Nutzer der ExoPlayer-Bibliotheken 1 und 2:

  1. Ein Upgrade von der ExoPlayer-Version kann dazu führen, dass die Kompilierung des Codes beendet wird.
  2. Bei einer App, die sowohl direkt als auch über eine Zwischenbibliothek vom ExoPlayer abhing, musste darauf geachtet werden, dass beide Abhängigkeiten dieselbe Version hatten. Andernfalls konnten Inkompatibilitäten bei Binärdateien zu Laufzeitabstürzen führen.

Verbesserungen in Media3

Media3 garantiert die Binärkompatibilität für einen Teil 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 ausgegeben, es sei denn, sie sind mit @OptIn annotiert.

Nach der Migration von ExoPlayer v2 zu Media3 werden möglicherweise viele Instabilitätsfehler bei der API-Lint-Prüfung angezeigt. Dadurch kann es so aussehen, als wäre Media3 weniger stabil als ExoPlayer v2. Das ist 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 einfach darin, dass Sie bei einem Lint-Fehler jetzt auf die verschiedenen Stabilitätsstufen hingewiesen werden.

Instabile API-Lint-Fehler verarbeiten

Im Abschnitt zur Fehlerbehebung für diese Lint-Fehler finden Sie weitere Informationen dazu, wie Sie die Verwendung instabiler APIs in Java- und Kotlin-Code mit @OptIn annotieren.

Eingestellte APIs

In Android Studio sind Aufrufe veralteter 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 angegeben ist, welche API stattdessen verwendet werden soll.

Screenshot: JavaDoc mit einer Alternative zur veralteten Methode anzeigen
Abbildung 3: Die JavaDoc-Kurzinfo in Android Studio schlägt für jedes nicht mehr unterstützte Symbol eine Alternative vor.

Codebeispiele und Demo-Apps

  • AndroidX Media3-Demo-App für Sitzungen (Mobilgeräte und WearOS)
    • Benutzerdefinierte Aktionen
    • System-UI-Benachrichtigung, Media-Taste/BT
    • Wiedergabesteuerung mit Google Assistant
  • UAMP: Android Media Player (branch media3) (Mobilgerät, AutomotiveOS)
    • System-UI-Benachrichtigung, Medientaste/BT, Wiedergabe fortsetzen
    • Wiedergabesteuerung über Google Assistant/WearOS
    • AutomotiveOS: Benutzerdefinierter Befehl und Anmeldung