Guida alla migrazione di AndroidX Media3

È opportuno eseguire la migrazione delle app che attualmente utilizzano la libreria com.google.android.exoplayer2 autonoma e androidx.media a androidx.media3. Utilizza lo script di migrazione per eseguire la migrazione dei file di build gradle, dei file di origine Java e Kotlin e dei file di layout XML da ExoPlayer 2.19.1 ad AndroidX Media3 1.1.1.

Panoramica

Prima di eseguire la migrazione, rivedi le sezioni seguenti per scoprire di più sui vantaggi delle nuove API, sulle API di cui eseguire la migrazione e sui prerequisiti che il progetto della tua app deve soddisfare.

Perché eseguire la migrazione a Jetpack Media3

  • È la nuova casa di ExoPlayer, mentre com.google.android.exoplayer2 è fuori produzione.
  • Accedi all'API del player tra componenti/processi con MediaBrowser/MediaController.
  • Utilizza le funzionalità estese delle API MediaSession e MediaController.
  • Pubblicizza le funzionalità di riproduzione con il controllo dell'accesso granulare.
  • Semplifica la tua app rimuovendo MediaSessionConnector e PlayerNotificationManager.
  • Compatibile con le versioni precedenti con le API client di Media-Comppat (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

API multimediali per la migrazione ad AndroidX Media3

  • ExoPlayer e relative estensioni
    Sono inclusi tutti i moduli del precedente progetto ExoPlayer, ad eccezione del modulo mediasession, che non è più disponibile. È possibile eseguire la migrazione di app o moduli a seconda dei pacchetti in com.google.android.exoplayer2 con lo script di migrazione.
  • MediaSessionConnector (a seconda dei androidx.media.* pacchetti di androidx.media:media:1.4.3+)
    Rimuovi MediaSessionConnector e utilizza invece androidx.media3.session.MediaSession.
  • MediaBrowserServiceCompat (a seconda dei androidx.media.* pacchetti di androidx.media:media:1.4.3+)
    Esegui la migrazione delle sottoclassi androidx.media.MediaBrowserServiceCompat a androidx.media3.session.MediaLibraryService e del codice utilizzando MediaBrowserCompat.MediaItem in androidx.media3.common.MediaItem.
  • MediaBrowserCompat (a seconda dei android.support.v4.media.* pacchetti di androidx.media:media:1.4.3+)
    Esegui la migrazione del codice client utilizzando MediaBrowserCompat o MediaControllerCompat per usare androidx.media3.session.MediaBrowser con androidx.media3.common.MediaItem.

Prerequisiti

  1. Assicurati che il progetto sia sotto il controllo del codice sorgente

    Assicurati di poter ripristinare facilmente le modifiche applicate dagli strumenti di migrazione basati su script. Se il tuo progetto non è ancora sotto il controllo del codice sorgente, è il momento giusto per iniziare. Se per qualche motivo non vuoi farlo, crea una copia di backup del progetto prima di iniziare la migrazione.

  2. Aggiornare la tua app

    • Ti consigliamo di aggiornare il progetto per utilizzare la versione più recente della libreria ExoPlayer e rimuovere eventuali chiamate a metodi deprecati. Se intendi utilizzare lo script per la migrazione, devi far corrispondere la versione a cui stai eseguendo l'aggiornamento con la versione gestita dallo script.

    • Aumenta il valore compileSdkVersion della tua app fino ad almeno 32.

    • Esegui l'upgrade di Gradle e del plug-in Android Studio Gradle a una versione recente che funzioni con le dipendenze aggiornate di cui sopra. Ad esempio:

      • Versione del plug-in Android Gradle: 7.1.0
      • Versione Gradle: 7.4
    • Sostituisci tutte le istruzioni di importazione con caratteri jolly che utilizzano un'asterix (*) e utilizza istruzioni di importazione complete: elimina le istruzioni di importazione con caratteri jolly e utilizza Android Studio per importare le istruzioni complete (F2 - Alt/Invio, F2 - Alt/Invio, ...).

    • Esegui la migrazione da com.google.android.exoplayer2.PlayerView a com.google.android.exoplayer2.StyledPlayerView. Questa operazione è necessaria perché non esiste un equivalente a com.google.android.exoplayer2.PlayerView in AndroidX Media3.

Migrazione di ExoPlayer con il supporto degli script

Lo script facilita il passaggio da com.google.android.exoplayer2 alla nuova struttura di pacchetti e moduli in androidx.media3. Lo script applica alcuni controlli di convalida sul progetto e visualizza degli avvisi se la convalida non va a buon fine. In caso contrario, applica le mappature di classi e pacchetti rinominati nelle risorse di un progetto gradle Android scritto in Java o Kotlin.

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

Utilizzo dello script di migrazione

  1. Scarica lo script di migrazione dal tag del progetto ExoPlayer su GitHub corrispondente alla versione in cui hai aggiornato l'app:

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. Rendi eseguibile lo script:

    chmod 744 media3-migration.sh
    
  3. Esegui lo script con --help per scoprire le opzioni.

  4. Esegui lo script con -l per elencare il set di file selezionati per la migrazione (utilizza -f per forzare la scheda senza avvisi):

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. Esegui lo script con -m per mappare pacchetti, classi e moduli a Media3. L'esecuzione dello script con l'opzione -m applicherà le modifiche ai file selezionati.

    • Fermati all'errore di convalida senza apportare modifiche
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • Esecuzione forzata

    Se lo script rileva una violazione dei prerequisiti, la migrazione può essere forzata con il flag -f:

    ./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

Completa questi passaggi manuali dopo aver eseguito lo script con l'opzione -m:

  1. Verifica in che modo lo script ha modificato il tuo codice: utilizza uno strumento per le differenze e risolvi i potenziali problemi (valuta la possibilità di inviare un bug se ritieni che lo script abbia un problema generale che è stato introdotto senza trasmettere l'opzione -f).
  2. Crea il progetto: usa ./gradlew clean build oppure in Android Studio scegli File > Sincronizza progetto con file Gradle, poi Crea > Pulisci progetto e poi Crea > Ricrea progetto (monitora la build nella scheda "Build - Build Output" di Android Studio.

Passaggi successivi consigliati:

  1. Risolvi l'attivazione per gli errori relativi all'utilizzo di API instabili.
  2. Sostituisci le chiamate API ritirate: utilizza l'API sostitutiva suggerita. Tieni premuto il puntatore sull'avviso in Android Studio e consulta il JavaDoc del simbolo obsoleto per scoprire cosa utilizzare al posto di una determinata chiamata.
  3. Ordina le istruzioni di importazione: apri il progetto in Android Studio, poi fai clic con il tasto destro del mouse su un nodo della cartella di un pacchetto nel visualizzatore del progetto e scegli Importazioni Optimize sui pacchetti che contengono i file di origine modificati.

Sostituisci MediaSessionConnector con androidx.media3.session.MediaSession

Nel mondo precedente di MediaSessionCompat, MediaSessionConnector era responsabile della sincronizzazione dello stato del player con lo stato della sessione e della ricezione di comandi dai controller che dovevano essere delegati ai metodi appropriati dei player. Con AndroidX Media3, questa operazione viene eseguita da MediaSession direttamente senza bisogno di un connettore.

  1. Rimuovi tutti i riferimenti e l'utilizzo di MediaSessionConnector: se hai utilizzato lo script automatico per eseguire la migrazione di classi e pacchetti ExoPlayer, è probabile che lo script abbia lasciato il codice in uno stato non compilabile in relazione a MediaSessionConnector che non può essere risolto. Android Studio ti mostrerà il codice non funzionante quando proverai a creare o avviare l'app.

  2. Nel file build.gradle in cui gestisci le dipendenze, aggiungi una dipendenza per l'implementazione al modulo della sessione AndroidX Media3 e rimuovi la dipendenza precedente:

    implementation "androidx.media3:media3-session:1.3.1"
    
  3. Sostituisci MediaSessionCompat con androidx.media3.session.MediaSession.

  4. Nel sito del codice in cui hai creato la versione precedente di MediaSessionCompat, utilizza androidx.media3.session.MediaSession.Builder per creare una MediaSession. Supera il giocatore per creare il generatore di sessioni.

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player)
        .setSessionCallback(MySessionCallback())
        .build()
    
  5. Implementa MySessionCallback come richiesto dall'app. Questa operazione è facoltativa. Se vuoi consentire ai controller di aggiungere elementi multimediali al player, implementa MediaSession.Callback.onAddMediaItems(). Offre vari metodi API attuali e legacy che aggiungono elementi multimediali al player per la riproduzione in modo compatibile con le versioni precedenti. Sono inclusi i metodi MediaController.set/addMediaItems() del controller Media3 e i metodi TransportControls.prepareFrom*/playFrom* dell'API legacy. Un'implementazione di esempio di onAddMediaItems è disponibile nella sezione PlaybackService dell'app demo della sessione.

  6. Rilascia la sessione multimediale sul sito del codice in cui hai eliminato la sessione prima della migrazione:

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

Funzionalità di MediaSessionConnector in Media3

La seguente tabella mostra le API Media3 che gestiscono le funzionalità implementate in precedenza in MediaSessionConnector.

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setCustomLayout()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() è chiamato internamente)
QueueNavigator ForwardingPlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

Esegui la migrazione di MediaBrowserService a MediaLibraryService

AndroidX Media3 introduce MediaLibraryService che sostituisce MediaBrowserServiceCompat. JavaDoc di MediaLibraryService e la sua super classe MediaSessionService forniscono una buona introduzione all'API e al modello di programmazione asincrono del servizio.

MediaLibraryService è compatibile con le versioni precedenti di MediaBrowserService. Un'app client che utilizza MediaBrowserCompat o MediaControllerCompat continua a funzionare senza modifiche al codice durante la connessione a MediaLibraryService. Per un client, non è chiaro se la tua app utilizza MediaLibraryService o una versione precedente di MediaBrowserServiceCompat.

Diagramma del componente dell&#39;app con app di servizi, attività e esterne.
Figura 1: panoramica dei componenti dell'app multimediale
  1. Affinché la compatibilità con le versioni precedenti funzioni, devi registrare entrambe le interfacce di servizio con il tuo servizio in AndroidManifest.xml. In questo modo, un client trova il tuo servizio tramite l'interfaccia di servizio richiesta:

    <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. Nel file build.gradle in cui gestisci le dipendenze, aggiungi una dipendenza di implementazione al modulo di sessione AndroidX Media3 e rimuovi la dipendenza precedente:

    implementation "androidx.media3:media3-session:1.3.1"
    
  3. Cambia il servizio in modo che erediti da un MediaLibraryService anziché MediaBrowserService Come detto in precedenza, MediaLibraryService è compatibile con la versione precedente MediaBrowserService. Di conseguenza, l'API più ampia che il servizio offre ai clienti rimane la stessa. Pertanto, è probabile che un'app possa mantenere la maggior parte della logica necessaria per implementare MediaBrowserService e adattarla al nuovo MediaLibraryService.

    Le principali differenze rispetto all'esperienza MediaBrowserServiceCompat precedente sono le seguenti:

    • Implementa i metodi del ciclo di vita del servizio: i metodi di cui è necessario eseguire l'override sul servizio stesso sono onCreate/onDestroy, in cui un'app alloca/rilascia la sessione della libreria, il player e altre risorse. Oltre ai metodi standard del ciclo di vita del servizio, un'app deve eseguire l'override di onGetSession(MediaSession.ControllerInfo) per restituire il valore MediaLibrarySession creato in onCreate.

    • Implementa MediaLibraryService.MediaLibrarySessionCallback: la creazione di una sessione richiede un MediaLibraryService.MediaLibrarySessionCallback che implementi i metodi effettivi dell'API del dominio. Di conseguenza, anziché eseguire l'override dei metodi API del servizio legacy, eseguirai l'override dei metodi di MediaLibrarySession.Callback.

      Il callback viene quindi utilizzato per creare MediaLibrarySession:

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

      Trova l'API completa di MediaLibrarySessionCallback nella documentazione dell'API.

    • Implementazione di MediaSession.Callback.onAddMediaItems(): il callback onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) pubblica vari metodi API attuali e precedenti che aggiungono elementi multimediali al player per riprodurli in modo compatibile con le versioni precedenti. Sono inclusi i metodi MediaController.set/addMediaItems() del controller Media3 e i metodi TransportControls.prepareFrom*/playFrom* dell'API legacy. Un esempio di implementazione del callback è disponibile nella sezione PlaybackService dell'app demo della sessione.

    • AndroidX Media3 utilizza androidx.media3.common.MediaItem anziché MediaBrowserCompat.MediaItem e MediaMetadataCompat. Parti del codice associate alle classi legacy devono essere modificate di conseguenza o mappate al valore MediaItem di Media3.

    • Il modello di programmazione asincrono generale è stato modificato in Futures in contrasto con l'approccio Result scollegabile di MediaBrowserServiceCompat. L'implementazione del servizio può restituire un ListenableFuture asincrono anziché scollegare un risultato o restituire un Future immediato per restituire direttamente un valore.

Rimuovi PlayerNotificationManager

L'MediaLibraryService supporta automaticamente le notifiche di contenuti multimediali e la PlayerNotificationManager può essere rimossa quando utilizzi un MediaLibraryService o un MediaSessionService.

Un'app può personalizzare la notifica impostando un MediaNotification.Provider personalizzato in onCreate() che sostituisce il DefaultMediaNotificationProvider. MediaLibraryService si occupa quindi di avviare il servizio in primo piano come richiesto.

Se esegui l'override di MediaLibraryService.updateNotification(), un'app può acquisire ulteriormente la proprietà della pubblicazione di una notifica e l'avvio/l'interruzione del servizio in primo piano a seconda delle esigenze.

Migrazione del codice client utilizzando un MediaBrowser

Con AndroidX Media3, un MediaBrowser implementa le interfacce MediaController/Player e può essere utilizzato per controllare la riproduzione di contenuti multimediali oltre a sfogliare la raccolta multimediale. Se dovevi creare un MediaBrowserCompat e un MediaControllerCompat nel mondo precedente, puoi fare lo stesso utilizzando solo MediaBrowser in Media3.

È possibile creare un elemento MediaBrowser e attendere la connessione al servizio che viene stabilita:

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

Consulta l'articolo Controllare la riproduzione nella sessione multimediale per scoprire come creare MediaController per controllare la riproduzione in background.

Ulteriori passaggi e pulizia

Errori relativi all'API instabile

Dopo la migrazione a Media3, potresti visualizzare errori di lint relativi a un uso instabile dell'API. Queste API sono sicure da usare e gli errori di lint sono un sottoprodotto delle nostre nuove garanzie di compatibilità binaria. Se non hai bisogno di una rigorosa compatibilità binaria, questi errori possono essere eliminati in sicurezza con un'annotazione @OptIn.

Premessa

Né ExoPlayer v1 né v2 hanno fornito rigide garanzie sulla compatibilità binaria della libreria tra le versioni successive. La superficie dell'API ExoPlayer è molto progettata, in modo da consentire alle app di personalizzare quasi ogni aspetto della riproduzione. Le versioni successive di ExoPlayer occasionalmente introdussero nomi di simboli o altre modifiche che provocano errori (ad esempio, nuovi metodi obbligatori sulle interfacce). Nella maggior parte dei casi queste interruzioni sono state mitigate introducendo il nuovo simbolo e ritirando quello vecchio per alcune versioni, per dare agli sviluppatori il tempo di eseguire la migrazione dei loro utilizzi, ma non sempre è stato possibile.

Queste modifiche che provocano errori hanno causato due problemi agli utenti delle librerie ExoPlayer v1 e v2:

  1. Un upgrade alla versione ExoPlayer potrebbe causare l'interruzione della compilazione del codice.
  2. Un'app che dipendeva da ExoPlayer sia direttamente che tramite una libreria intermedia doveva garantire che entrambe le dipendenze fossero la stessa versione, altrimenti le incompatibilità binarie potrebbero causare arresti anomali di runtime.

Miglioramenti in Media3

Media3 garantisce la compatibilità binaria per un sottoinsieme della piattaforma API. Le parti che non garantiscono la compatibilità binaria sono contrassegnate con @UnstableApi. Per fare questa distinzione, gli utilizzi di simboli API instabili generano un errore lint a meno che non siano annotati con @OptIn.

Dopo la migrazione da ExoPlayer v2 a Media3, potresti notare molti errori di lint dell'API instabili. Questo può far sembrare che Media3 sia "meno stabile" di ExoPlayer v2. Non è così. Le parti "instabili" dell'API Media3 hanno lo stesso livello di stabilità dell'intero della superficie API ExoPlayer v2 e le garanzie della superficie stabile dell'API Media3 non sono disponibili in ExoPlayer v2. La differenza è semplicemente che un errore lint ora ti avvisa dei diversi livelli di stabilità.

Gestire gli errori di lint dell'API instabile

Sono disponibili due opzioni per gestire gli errori lint dell'API instabile:

  • Passa a un'API stabile che ottenga lo stesso risultato.
  • Continua a utilizzare l'API unstable e annota l'utilizzo con @OptIn.

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

    Tieni presente che esiste anche un'annotazione kotlin.OptIn che non deve essere utilizzata. A questo scopo, è importante attenersi all'annotazione androidx.annotation.OptIn.

    Screenshot: come aggiungere l&#39;annotazione di attivazione
    Figura 2: aggiunta di un'annotazione @androidx.annotations.optIn con Android Studio.

È possibile attivare interi pacchetti aggiungendo un package-info.java:

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

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

È possibile attivare interi progetti eliminando l'errore lint specifico nel file lint.xml. Per ulteriori dettagli, consulta JavaDoc dell'annotazione UnstableApi.

API deprecate

Potresti notare che le chiamate alle API deprecate vengono applicate in Android Studio. Ti consigliamo di sostituire queste chiamate con l'alternativa appropriata. Passa il mouse sopra il simbolo per visualizzare il JavaDoc che indica l'API da utilizzare.

Screenshot: come visualizzare JavaDoc con un&#39;alternativa al metodo obsoleto
Figura 3: la descrizione comando di JavaDoc in Android Studio suggerisce un'alternativa a qualsiasi simbolo obsoleto.

Esempi di codice e app demo

  • App demo per la sessione AndroidX Media3 (dispositivi mobili e WearOS)
    • Azioni personalizzate
    • Notifica UI di sistema, MediaButton/BT
    • Controllo di riproduzione dell'Assistente Google
  • UAMP: Android Media Player (branch media3) (dispositivi mobili, AutomotiveOS)
    • Notifica UI di sistema, MediaButton/BT, ripresa della riproduzione
    • Controllo di riproduzione dell'Assistente Google/WearOS
    • AutomotiveOS: comando e accesso personalizzati