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. 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 vor der Migration in den folgenden Abschnitten mehr über die Vorteile der neuen APIs, die zu migrierenden APIs und die Voraussetzungen, die das Projekt Ihrer Anwendung erfüllen muss.

Vorteile der Migration zu Jetpack Media3

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

Media APIs für die Migration zu AndroidX Media3

  • ExoPlayer und seine Erweiterungen
    Hierzu zählen alle Module des alten ExoPlayer-Projekts mit Ausnahme des eingestellten Moduls mediasession. Anwendungen 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 stattdessen 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 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 den androidx.media3.session.MediaBrowser mit androidx.media3.common.MediaItem zu verwenden.

Voraussetzungen

  1. Prüfen, ob Ihr Projekt der Versionsverwaltung unterliegt

    Stellen Sie sicher, dass Sie Änderungen, die mit skriptbasierten Migrationstools angewendet wurden, einfach rückgängig machen können. 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 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 für die Migration das Skript verwenden möchten, müssen Sie die Version, auf die Sie aktualisieren, mit der Version abgleichen, 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 einen Stern (*) verwenden, und verwenden Sie voll qualifizierte Importanweisungen: Löschen Sie die Platzhalter-Importanweisungen und verwenden Sie Android Studio, um die vollständig qualifizierten Anweisungen zu importieren (F2 – Alt/Eingabetaste, F2 – Alt/Eingabetaste, ...).

    • Migrieren Sie von com.google.android.exoplayer2.PlayerView zu com.google.android.exoplayer2.StyledPlayerView. Dies ist notwendig, da es kein Äquivalent für com.google.android.exoplayer2.PlayerView in AndroidX Media3 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 von umbenannten Klassen und Paketen 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 Anwendung 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 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 Validierungsfehler stoppen, 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 nach der Ausführung des Skripts mit der Option -m die folgenden manuellen Schritte aus:

  1. Prüfen, wie der Code durch das Skript geändert wurde: Verwenden Sie ein diff-Tool und beheben Sie potenzielle Probleme. Wenn Sie der Meinung sind, dass beim Skript ein allgemeines Problem aufgetreten ist, ohne die Option -f zu übergeben, können Sie einen Fehler melden.
  2. Erstellen Sie das Projekt: Verwenden Sie entweder ./gradlew clean build oder wählen Sie in Android Studio File > Sync Project with Gradle Files > Build > Clean project und dann Build > Rebuild project aus. Überwachen Sie Ihren Build auf dem Tab Build – Build Output von Android Studio.

Empfohlene weitere Schritte:

  1. Beheben Sie Opt-in-Fehler bei der Verwendung instabiler APIs.
  2. Veraltete API-Aufrufe ersetzen: Verwenden Sie die vorgeschlagene Ersatz-API. Halten Sie den Mauszeiger über die Warnung in Android Studio und sehen Sie in JavaDoc für das verworfene Symbol nach, um herauszufinden, was Sie anstelle eines bestimmten Aufrufs verwenden können.
  3. Importanweisungen sortieren: Öffnen Sie das Projekt in Android Studio, klicken Sie dann in der Projektanzeige 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 Legacy-MediaSessionCompat-Umgebung war die MediaSessionConnector dafür verantwortlich, den Status des Spielers mit dem Status der Sitzung zu synchronisieren und Befehle von Controllern zu empfangen, die eine Delegation an geeignete Spielermethoden erforderten. Bei AndroidX Media3 erfolgt dies über MediaSession direkt, ohne dass ein Connector erforderlich ist.

  1. Alle Verweise und Nutzung von MediaSessionConnector entfernen:Wenn Sie das automatisierte Skript zur Migration von ExoPlayer-Klassen und -Paketen verwendet haben, hat das Skript den Code wahrscheinlich in einem nicht kompilierbaren Status für MediaSessionConnector belassen, der 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 das alte MediaSessionCompat erstellt haben, androidx.media3.session.MediaSession.Builder, um 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 Sie Controllern erlauben möchten, dem Player Medienelemente hinzuzufügen, müssen Sie MediaSession.Callback.onAddMediaItems() implementieren. Sie bietet verschiedene aktuelle und alte API-Methoden, die dem Player Medienelemente hinzufügen, damit diese abwärtskompatibel sind. 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-Funktion in Media3

In der folgenden Tabelle sind die Media3 APIs aufgeführt, die die zuvor in MediaSessionConnector implementierten Funktionen übernehmen.

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

AndroidX Media3 führt MediaLibraryService ein, 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 beim Herstellen einer Verbindung zu MediaLibraryService weiterhin ohne Codeänderungen. Für einen Client ist es transparent, ob Ihre Anwendung ein MediaLibraryService- oder ein Legacy-MediaBrowserServiceCompat verwendet.

Diagramm der App-Komponenten mit Dienst, Aktivität und externen Apps
Abbildung 1: Übersicht über Medien-App-Komponenten
  1. Damit die Abwärtskompatibilität funktioniert, müssen Sie beide Dienstschnittstellen bei 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.3.1"
    
  3. Ändern Sie Ihren Dienst so, dass er von einer MediaLibraryService anstelle von MediaBrowserService übernommen wird. Wie bereits erwähnt, ist der MediaLibraryService mit dem Legacy-MediaBrowserService kompatibel. Entsprechend ist die API, die der Dienst für Kunden anbietet, immer noch dieselbe. Daher ist es wahrscheinlich, dass eine Anwendung den größten Teil der Logik beibehalten kann, die zum Implementieren von MediaBrowserService erforderlich ist, und sie für das neue MediaLibraryService anpassen kann.

    Die Hauptunterschiede im Vergleich zum Legacy-MediaBrowserServiceCompat-Objekt sind:

    • Methoden für den Dienstlebenszyklus implementieren: Die Methoden, die im Dienst selbst überschrieben werden müssen, sind onCreate/onDestroy. Dabei weist eine Anwendung die Bibliothekssitzung, den Player und andere Ressourcen zu bzw. frei. Zusätzlich zu den Standardmethoden für den Dienstlebenszyklus muss eine Anwendung 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 tatsächlichen API-Methoden der Domain implementiert. Statt die API-Methoden des alten Dienstes zu überschreiben, werden stattdessen 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>) verwendet verschiedene aktuelle und Legacy-API-Methoden, die dem Player Medienelemente hinzufügen, damit sie abwärtskompatibel wiedergegeben 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 des Callbacks findest du in der PlaybackService der Sitzungs-Demo-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 werden oder stattdessen dem Media3-MediaItem zugeordnet werden.

    • Das allgemeine asynchrone Programmiermodell wurde im Gegensatz zum abnehmbaren Result-Ansatz von MediaBrowserServiceCompat in Futures geändert. Die 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 du MediaLibraryService oder MediaSessionService verwendest.

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

Durch das Überschreiben von MediaLibraryService.updateNotification() kann eine App weiterhin die volle Verantwortung dafür übernehmen, eine Benachrichtigung zu posten und den Dienst im Vordergrund nach Bedarf zu starten oder zu beenden.

Clientcode mit einem MediaBrowser migrieren

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

Ein MediaBrowser kann erstellt werden und warten, bis die Verbindung zum Dienst hergestellt wird:

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 werden möglicherweise Lint-Fehler zu instabilen API-Nutzungen angezeigt. Diese APIs sind sicher und die Lint-Fehler sind ein Nebenprodukt unserer neuen binären Kompatibilitätsgarantien. 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 strikte Garantien hinsichtlich der 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 Schnittstellen). In den meisten Fällen wurden diese Probleme durch die Einführung des neuen Symbols und die Abschaffung des alten Symbols für einige Versionen ausgeglichen, um Entwicklern Zeit zu geben, ihre Nutzungen zu migrieren. Dies war jedoch nicht immer möglich.

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

  1. Ein Upgrade von der ExoPlayer-Version kann dazu führen, dass die Kompilierung des Codes beendet wird.
  2. Eine App, die sowohl direkt als auch über eine Zwischenbibliothek von ExoPlayer abhängig war, musste sicherstellen, dass beide Abhängigkeiten zur selben Version waren. Andernfalls könnten binäre Inkompatibilitäten zu Laufzeitabstürzen führen.

Verbesserungen bei Media3

Media3 garantiert die binäre Kompatibilität für einen Teil der API-Oberfläche. Die Teile, die die Binärkompatibilität nicht garantieren, sind mit @UnstableApi gekennzeichnet. Um diese Unterscheidung zu verdeutlichen, generieren instabile API-Symbole einen Lint-Fehler, sofern sie nicht mit @OptIn gekennzeichnet sind.

Nach der Migration von ExoPlayer v2 zu Media3 werden möglicherweise viele instabile API-Lint-Fehler angezeigt. Dadurch kann der Eindruck entstehen, dass Media3 „weniger stabil“ ist als ExoPlayer v2. Das ist nicht der Fall. Die "instabilen" Teile der Media3 API haben dieselbe Stabilität wie die ganze Oberfläche der ExoPlayer v2 API. Die Garantien für die stabile Media3 API-Oberfläche sind in ExoPlayer v2 überhaupt nicht verfügbar. Der Unterschied besteht lediglich darin, dass ein Lint-Fehler Sie jetzt über die verschiedenen Stabilitätsstufen informiert.

Instabile API-Lint-Fehler verarbeiten

Weitere Informationen dazu, wie Sie die Java- und Kotlin-Verwendung von instabilen APIs mit @OptIn annotieren, finden Sie im Abschnitt zur Fehlerbehebung zu diesen Lint-Fehlern.

Verworfene APIs

Aufrufe von verworfenen APIs sind in Android Studio durchgestrichen. Wir empfehlen, solche Aufrufe durch die entsprechende Alternative zu ersetzen. Bewegen Sie den Mauszeiger auf das Symbol, um das JavaDoc zu sehen, in dem angegeben wird, welche API stattdessen verwendet werden soll.

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

Codebeispiele und Demo-Apps

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