Guida alla migrazione di AndroidX Media3

Le app che attualmente utilizzano la libreria com.google.android.exoplayer2 autonoma e androidx.media devono eseguire la migrazione a androidx.media3. Utilizza lo script di migrazione per eseguire la migrazione dei file di build Gradle, dei file sorgente 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, esamina le sezioni seguenti per scoprire di più sui vantaggi delle nuove API, sulle API da migrare e sui prerequisiti che il progetto della tua app deve soddisfare.

Perché eseguire la migrazione a Jetpack Media3

  • È la nuova sede di ExoPlayer, mentre com.google.android.exoplayer2 non è più supportato.
  • Accedi all'API Player in tutti i 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 delle API client media-compat (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

API per il settore dei media da migrare ad AndroidX Media3

  • ExoPlayer e le relative estensioni
    Sono inclusi tutti i moduli del progetto ExoPlayer precedente, ad eccezione del modulo mediasession, che non è più disponibile. Le app o i moduli che dipendono dai pacchetti in com.google.android.exoplayer2 possono essere migrati con lo script di migrazione.
  • MediaSessionConnector (a seconda dei pacchetti androidx.media.* di androidx.media:media:1.4.3+)
    Rimuovi MediaSessionConnector e utilizza androidx.media3.session.MediaSession.
  • MediaBrowserServiceCompat (a seconda dei pacchetti androidx.media.* di androidx.media:media:1.4.3+)
    Esegui la migrazione delle sottoclassi di androidx.media.MediaBrowserServiceCompat a androidx.media3.session.MediaLibraryService e del codice che utilizza MediaBrowserCompat.MediaItem a androidx.media3.common.MediaItem.
  • MediaBrowserCompat (a seconda dei pacchetti android.support.v4.media.* di androidx.media:media:1.4.3+)
    Esegui la migrazione del codice client utilizzando MediaBrowserCompat o MediaControllerCompat per utilizzare 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 annullare facilmente le modifiche applicate dagli strumenti di migrazione basati su script. Se non hai ancora il tuo progetto sotto il controllo della versione, ora è un buon momento 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 in modo da utilizzare la versione più recente della libreria ExoPlayer e rimuovere eventuali chiamate a metodi ritirati. Se intendi utilizzare lo script per la migrazione, devi abbinare la versione a cui stai eseguendo l'aggiornamento con la versione gestita dallo script.

    • Aumenta il valore compileSdkVersion della tua app almeno a 32.

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

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

    • Esegui la migrazione da com.google.android.exoplayer2.PlayerView a com.google.android.exoplayer2.StyledPlayerView. Questo è necessario perché non esiste un equivalente di com.google.android.exoplayer2.PlayerView in AndroidX Media3.

Esegui la 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 al progetto e stampa avvisi se la convalida non riesce. In caso contrario, applica i mapping delle classi e dei pacchetti rinominati nelle risorse di un progetto Gradle per 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 a 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 l'insieme di file selezionati per la migrazione (utilizza -f per forzare l'elenco 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.

    • Arresta in caso di 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. Controlla in che modo lo script ha modificato il codice: utilizza uno strumento di confronto e correggi i potenziali problemi (valuta la possibilità di segnalare un bug se ritieni che lo script abbia un problema generale introdotto senza superare l'opzione -f).
  2. Compila il progetto: utilizza ./gradlew clean build o in Android Studio scegli File > Sync Project with Gradle Files, poi Build > Clean project e poi Build > Rebuild project (monitora la build nella scheda "Build - Build Output" di Android Studio.

Passaggi di follow-up consigliati:

  1. Risolvi l'attivazione per gli errori relativi all'utilizzo di API instabili.
  2. Sostituisci le chiamate API obsolete: utilizza l'API di sostituzione suggerita. Tieni il puntatore sopra l'avviso in Android Studio e consulta il JavaDoc del simbolo ritirato 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 del pacchetto nel visualizzatore del progetto e scegli Ottimizza importazioni sui pacchetti che contengono i file sorgente modificati.

Sostituisci MediaSessionConnector con androidx.media3.session.MediaSession

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

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

  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 legacy:

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

  4. Nel sito del codice in cui hai creato il MediaSessionCompat legacy, utilizza androidx.media3.session.MediaSession.Builder per creare un MediaSession. Passa il giocatore per creare lo strumento per la creazione di sessioni.

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player)
        .setSessionCallback(MySessionCallback())
        .build()
    
  5. Implementa MySessionCallback come richiesto dalla tua app. Questa opzione è facoltativa. Se vuoi consentire ai controller di aggiungere elementi multimediali al lettore, 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, nonché i metodi TransportControls.prepareFrom*/playFrom* dell'API legacy. Un'implementazione di esempio di onAddMediaItems è disponibile nel PlaybackService dell'app demo della sessione.

  6. Rilascia la sessione multimediale nel sito del codice in cui l'hai distrutta prima della migrazione:

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

Funzionalità MediaSessionConnector in Media3

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

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() viene chiamato internamente)
QueueNavigator ForwardingSimpleBasePlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

Migrazione di MediaBrowserService a MediaLibraryService

AndroidX Media3 introduce MediaLibraryService che sostituisce MediaBrowserServiceCompat. Il JavaDoc di MediaLibraryService e della relativa superclasse MediaSessionService fornisce una buona introduzione all'API e al modello di programmazione asincrona 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 quando si connette a un MediaLibraryService. Per un client, è trasparente se la tua app utilizza un MediaLibraryService o un MediaBrowserServiceCompat legacy.

Diagramma dei componenti dell&#39;app con servizio, attività e app esterne.
Figura 1: panoramica dei componenti dell'app multimediale
  1. Per il funzionamento della compatibilità con le versioni precedenti, 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 legacy:

    implementation "androidx.media3:media3-session:1.7.1"
    
  3. Modifica il servizio in modo che erediti da un MediaLibraryService anziché da MediaBrowserService Come detto in precedenza, MediaLibraryService è compatibile con MediaBrowserService legacy. Di conseguenza, l'API più ampia che il servizio offre ai client rimane invariata. 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 alla versione precedente MediaBrowserServiceCompat sono le seguenti:

    • Implementa i metodi del ciclo di vita del servizio:i metodi che devono essere sottoposti a override nel servizio stesso sono onCreate/onDestroy, dove 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 MediaLibrarySession creato in onCreate.

    • Implementa MediaLibraryService.MediaLibrarySessionCallback: la creazione di una sessione richiede un MediaLibraryService.MediaLibrarySessionCallback che implementi i metodi dell'API di dominio effettiva. Pertanto, 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.

    • Implementa MediaSession.Callback.onAddMediaItems(): il callback onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) gestisce 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, nonché i metodi TransportControls.prepareFrom*/playFrom* dell'API legacy. Un'implementazione di esempio del callback è disponibile in PlaybackService dell'app demo della sessione.

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

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

Rimuovi PlayerNotificationManager

MediaLibraryService supporta automaticamente le notifiche multimediali e PlayerNotificationManager può essere rimosso quando si utilizza MediaLibraryService o MediaSessionService.

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

Eseguendo l'override di MediaLibraryService.updateNotification(), un'app può assumere la piena proprietà della pubblicazione di una notifica e dell'avvio/interruzione del servizio in primo piano in base alle esigenze.

Eseguire la 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 dei contenuti multimediali oltre a sfogliare la libreria multimediale. Se dovevi creare un MediaBrowserCompat e un MediaControllerCompat nel mondo legacy, puoi fare lo stesso utilizzando solo MediaBrowser in Media3.

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

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 la sezione Controllare la riproduzione nella sessione multimediale per scoprire come creare un MediaController per controllare la riproduzione in background.

Ulteriori passaggi e pulizia

Errori API instabili

Dopo la migrazione a Media3, potresti visualizzare errori di lint relativi a utilizzi di API instabili. Queste API sono sicure da usare e gli errori di lint sono un sottoprodotto delle nostre nuove garanzie di compatibilità binaria. Se non è necessaria una compatibilità binaria rigorosa, questi errori possono essere eliminati in modo sicuro con un'annotazione @OptIn.

Sfondo

Né ExoPlayer v1 né v2 fornivano garanzie rigorose sulla compatibilità binaria della libreria tra le versioni successive. La superficie dell'API ExoPlayer è molto ampia per progettazione, per consentire alle app di personalizzare quasi ogni aspetto della riproduzione. Le versioni successive di ExoPlayer introducevano occasionalmente ridenominazioni di simboli o altre modifiche che causavano interruzioni (ad es. nuovi metodi richiesti nelle interfacce). Nella maggior parte dei casi, questi problemi sono stati mitigati introducendo il nuovo simbolo insieme al ritiro di quello precedente per alcune versioni, per consentire agli sviluppatori di eseguire la migrazione dei loro utilizzi, ma ciò non è sempre stato possibile.

Queste modifiche che causano interruzioni hanno comportato due problemi per gli utenti delle librerie ExoPlayer v1 e v2:

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

Miglioramenti in Media3

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

Dopo la migrazione da ExoPlayer v2 a Media3, potresti visualizzare molti errori di linting dell'API instabile. Questo potrebbe far sembrare Media3 "meno stabile" di ExoPlayer v2. Non è questo il caso. Le parti "instabili" dell'API Media3 hanno lo stesso livello di stabilità dell'intera superficie dell'API ExoPlayer v2 e le garanzie della superficie dell'API Media3 stabile non sono disponibili in ExoPlayer v2. La differenza è semplicemente che un errore di lint ora ti avvisa dei diversi livelli di stabilità.

Gestire gli errori di linting dell'API instabile

Per informazioni dettagliate su come annotare gli utilizzi di API instabili in Java e Kotlin con @OptIn, consulta la sezione relativa alla risoluzione dei problemi relativi a questi errori di lint.

API deprecate

Potresti notare che le chiamate alle API deprecate sono barrate 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 quale API utilizzare.

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

Esempi di codice e app demo

  • App demo della sessione AndroidX Media3 (mobile e WearOS)
    • Azioni personalizzate
    • Notifica dell'interfaccia utente di sistema, MediaButton/BT
    • Controllo della riproduzione dell'Assistente Google
  • UAMP: Android Media Player (branch media3) (mobile, AutomotiveOS)
    • Notifica dell'interfaccia utente di sistema, MediaButton/BT, ripresa della riproduzione
    • Controllo della riproduzione con l'Assistente Google/WearOS
    • AutomotiveOS: comando personalizzato e accesso