Panduan migrasi AndroidX Media3

Aplikasi yang saat ini menggunakan library com.google.android.exoplayer2 mandiri dan androidx.media harus bermigrasi ke androidx.media3. Gunakan skrip migrasi untuk memigrasikan file build gradle, file sumber Java dan Kotlin, serta file tata letak XML dari ExoPlayer 2.19.1 ke AndroidX Media3 1.1.1.

Ringkasan

Sebelum bermigrasi, tinjau bagian berikut untuk mempelajari lebih lanjut manfaat API baru, API yang akan dimigrasikan, dan prasyarat yang harus dipenuhi oleh project aplikasi Anda.

Alasan bermigrasi ke Jetpack Media3

  • Jetpack Media3 adalah rumah baru ExoPlayer, sedangkan com.google.android.exoplayer2 dihentikan.
  • Akses Player API di seluruh komponen/proses dengan MediaBrowser/MediaController.
  • Gunakan kemampuan yang diperluas dari API MediaSession dan MediaController.
  • Mengiklankan kemampuan pemutaran dengan kontrol akses yang sangat terperinci.
  • Sederhanakan aplikasi Anda dengan menghapus MediaSessionConnector dan PlayerNotificationManager.
  • Kompatibel dengan versi lama API klien media-compat (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

Media API yang akan dimigrasikan ke AndroidX Media3

  • ExoPlayer dan ekstensinya
    Ini mencakup semua modul project ExoPlayer lama, kecuali modul mediasession yang dihentikan. Aplikasi atau modul yang bergantung pada paket di com.google.android.exoplayer2 dapat dimigrasikan dengan skrip migrasi.
  • MediaSessionConnector (bergantung pada paket androidx.media.* dari androidx.media:media:1.4.3+)
    Hapus MediaSessionConnector dan gunakan androidx.media3.session.MediaSession sebagai gantinya.
  • MediaBrowserServiceCompat (bergantung pada paket androidx.media.* dari androidx.media:media:1.4.3+)
    Migrasikan subclass androidx.media.MediaBrowserServiceCompat ke androidx.media3.session.MediaLibraryService dan kode menggunakan MediaBrowserCompat.MediaItem ke androidx.media3.common.MediaItem.
  • MediaBrowserCompat (bergantung pada paket android.support.v4.media.* dari androidx.media:media:1.4.3+)
    Migrasikan kode klien menggunakan MediaBrowserCompat atau MediaControllerCompat untuk menggunakan androidx.media3.session.MediaBrowser dengan androidx.media3.common.MediaItem.

Prasyarat

  1. Pastikan project Anda berada di bawah kontrol sumber

    Pastikan Anda dapat dengan mudah mengembalikan perubahan yang diterapkan oleh alat migrasi yang menggunakan skrip. Jika Anda belum memiliki project di bawah kontrol sumber, sekarang adalah waktu yang tepat untuk memulainya. Jika karena alasan tertentu Anda tidak ingin melakukannya, buat salinan cadangan project Anda sebelum memulai migrasi.

  2. Mengupdate aplikasi

    • Sebaiknya update project Anda untuk menggunakan versi terbaru library ExoPlayer dan hapus semua panggilan ke metode yang tidak digunakan lagi. Jika Anda ingin menggunakan skrip untuk migrasi, Anda harus mencocokkan versi yang Anda update dengan versi yang ditangani oleh skrip.

    • Tingkatkan compileSdkVersion aplikasi Anda ke minimal 32.

    • Upgrade Gradle dan plugin Gradle Android Studio ke versi terbaru yang berfungsi dengan dependensi yang diupdate dari atas. Misalnya:

      • Versi Plugin Android Gradle: 7.1.0
      • Versi Gradle: 7.4
    • Ganti semua pernyataan impor karakter pengganti yang menggunakan tanda bintang (*) dan gunakan pernyataan impor yang sepenuhnya memenuhi syarat: Hapus pernyataan impor karakter pengganti dan gunakan Android Studio untuk mengimpor pernyataan yang sepenuhnya memenuhi syarat (F2 - Alt/Enter, F2 - Alt/Enter, ...).

    • Migrasikan dari com.google.android.exoplayer2.PlayerView ke com.google.android.exoplayer2.StyledPlayerView. Hal ini diperlukan karena tidak ada yang setara dengan com.google.android.exoplayer2.PlayerView di AndroidX Media3.

Memigrasikan ExoPlayer dengan dukungan skrip

Skrip ini memfasilitasi perpindahan dari com.google.android.exoplayer2 ke struktur paket dan modul baru di androidx.media3. Skrip ini menerapkan beberapa pemeriksaan validasi pada project Anda dan mencetak peringatan jika validasi gagal. Jika tidak, alat ini akan menerapkan pemetaan class dan paket yang diganti namanya dalam resource project Android Gradle yang ditulis dalam Java atau 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

Menggunakan skrip migrasi

  1. Download skrip migrasi dari tag project ExoPlayer di GitHub yang sesuai dengan versi yang telah Anda update untuk aplikasi Anda:

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. Buat agar skrip dapat dieksekusi:

    chmod 744 media3-migration.sh
    
  3. Jalankan skrip dengan --help untuk mempelajari opsi.

  4. Jalankan skrip dengan -l untuk mencantumkan kumpulan file yang dipilih untuk migrasi (gunakan -f untuk memaksa listingan tanpa peringatan):

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. Jalankan skrip dengan -m untuk memetakan paket, class, dan modul ke Media3. Menjalankan skrip dengan opsi -m akan menerapkan perubahan pada file yang dipilih.

    • Berhenti saat terjadi error validasi tanpa melakukan perubahan
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • Eksekusi paksa

    Jika skrip menemukan pelanggaran prasyarat, migrasi dapat dipaksakan dengan 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

Selesaikan langkah-langkah manual ini setelah menjalankan skrip dengan opsi -m:

  1. Periksa bagaimana skrip mengubah kode Anda: Gunakan alat diff dan perbaiki potensi masalah (pertimbangkan untuk melaporkan bug jika menurut Anda skrip memiliki masalah umum yang muncul tanpa meneruskan opsi -f).
  2. Build project: Gunakan ./gradlew clean build atau di Android Studio, pilih File > Sync Project with Gradle Files, lalu Build > Clean project, lalu Build > Rebuild project (pantau build Anda di tab 'Build - Build Output' di Android Studio).

Langkah-langkah tindak lanjut yang direkomendasikan:

  1. Selesaikan pilihan ikut serta untuk error terkait penggunaan API yang tidak stabil.
  2. Ganti panggilan API yang tidak digunakan lagi: Gunakan API pengganti yang disarankan. Tahan kursor di atas peringatan di Android Studio, dan lihat JavaDoc simbol yang tidak digunakan lagi untuk mengetahui apa yang harus digunakan, bukan panggilan tertentu.
  3. Urutkan pernyataan impor: Buka project di Android Studio, lalu klik kanan node folder paket di penampil project dan pilih Optimize imports pada paket yang berisi file sumber yang diubah.

Ganti MediaSessionConnector dengan androidx.media3.session.MediaSession

Di dunia MediaSessionCompat lama, MediaSessionConnector bertanggung jawab untuk menyinkronkan status pemutar dengan status sesi dan menerima perintah dari pengontrol yang memerlukan delegasi ke metode pemutar yang sesuai. Dengan AndroidX Media3, hal ini dilakukan oleh MediaSession secara langsung tanpa memerlukan konektor.

  1. Hapus semua referensi dan penggunaan MediaSessionConnector: Jika Anda menggunakan skrip otomatis untuk memigrasikan class dan paket ExoPlayer, skrip tersebut kemungkinan telah membuat kode Anda dalam status tidak dapat dikompilasi terkait MediaSessionConnector yang tidak dapat diselesaikan. Android Studio akan menunjukkan kode yang rusak saat Anda mencoba mem-build atau memulai aplikasi.

  2. Dalam file build.gradle tempat Anda mengelola dependensi, tambahkan dependensi implementasi ke modul sesi AndroidX Media3 dan hapus dependensi lama:

    implementation "androidx.media3:media3-session:1.11.1"
    
  3. Ganti MediaSessionCompat dengan androidx.media3.session.MediaSession.

  4. Di situs kode tempat Anda membuat MediaSessionCompat lama, gunakan androidx.media3.session.MediaSession.Builder untuk membuat MediaSession. Teruskan pemutar untuk membuat builder sesi.

    Kotlin

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player).setCallback(MySessionCallback()).build()

    Java

    ExoPlayer player = new ExoPlayer.Builder(context).build();
    mediaSession =
        new MediaSession.Builder(context, player).setCallback(new MySessionCallback()).build();

  5. Terapkan MySessionCallback sesuai kebutuhan aplikasi Anda. Langkah ini bersifat opsional. Jika Anda ingin mengizinkan pengontrol menambahkan item media ke pemutar, terapkan MediaSession.Callback.onAddMediaItems(). Class ini menyajikan berbagai metode API saat ini dan lama yang menambahkan item media ke pemutar untuk diputar dengan cara yang kompatibel dengan versi lama. Hal ini mencakup metode MediaController.set/addMediaItems() pengontrol Media3, serta metode TransportControls.prepareFrom*/playFrom* API lama. Contoh implementasi onAddMediaItems dapat ditemukan di PlaybackService aplikasi demo sesi.

  6. Rilis sesi media di situs kode tempat Anda menghancurkan sesi sebelum migrasi:

    Kotlin

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

    Java

    if (mediaSession != null) {
      mediaSession.getPlayer().release();
      mediaSession.release();
      mediaSession = null;
    }

Fungsi MediaSessionConnector di Media3

Tabel berikut menunjukkan Media3 API yang menangani fungsi yang sebelumnya diimplementasikan di MediaSessionConnector.

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() dipanggil secara internal)
QueueNavigator ForwardingSimpleBasePlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

Migrasikan MediaBrowserService ke MediaLibraryService

AndroidX Media3 memperkenalkan MediaLibraryService yang menggantikan MediaBrowserServiceCompat. JavaDoc MediaLibraryService dan superclass-nya, MediaSessionService, memberikan pengantar yang baik tentang API dan model pemrograman asinkron layanan ini.

MediaLibraryService kompatibel dengan versi lama MediaBrowserService. Aplikasi klien yang menggunakan MediaBrowserCompat atau MediaControllerCompat, akan terus berfungsi tanpa perubahan kode saat terhubung ke MediaLibraryService. Bagi klien, akan terlihat jelas apakah aplikasi Anda menggunakan MediaLibraryService atau MediaBrowserServiceCompat lama.

Diagram komponen aplikasi dengan layanan, aktivitas, dan aplikasi eksternal.
Gambar 1: Ringkasan komponen aplikasi media
  1. Agar kompatibilitas mundur berfungsi, Anda harus mendaftarkan kedua antarmuka layanan dengan layanan Anda di AndroidManifest.xml. Dengan cara ini, klien akan menemukan layanan Anda berdasarkan antarmuka layanan yang diperlukan:

    <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. Dalam file build.gradle tempat Anda mengelola dependensi, tambahkan dependensi implementasi ke modul sesi AndroidX Media3 dan hapus dependensi lama:

    implementation "androidx.media3:media3-session:1.11.1"
    
  3. Ubah layanan Anda agar diwarisi dari MediaLibraryService, bukan MediaBrowserService Seperti yang telah disebutkan sebelumnya, MediaLibraryService kompatibel dengan MediaBrowserService lama. Oleh karena itu, API yang lebih luas yang ditawarkan layanan kepada klien masih sama. Jadi, kemungkinan besar aplikasi dapat mempertahankan sebagian besar logika yang diperlukan untuk mengimplementasikan MediaBrowserService dan menyesuaikannya untuk MediaLibraryService baru.

    Perbedaan utama dibandingkan dengan MediaBrowserServiceCompat lama adalah sebagai berikut:

    • Mengimplementasikan metode siklus proses layanan: Metode yang perlu diganti pada layanan itu sendiri adalah onCreate/onDestroy, tempat aplikasi mengalokasikan/merilis sesi library, pemutar, dan resource lainnya. Selain metode siklus proses layanan standar, aplikasi perlu mengganti onGetSession(MediaSession.ControllerInfo) untuk menampilkan MediaLibrarySession yang dibuat di onCreate.

    • Mengimplementasikan MediaLibraryService.MediaLibrarySessionCallback: Membangun sesi memerlukan MediaLibraryService.MediaLibrarySessionCallback yang mengimplementasikan metode API domain yang sebenarnya. Jadi, alih-alih mengganti metode API layanan lama, Anda akan mengganti metode MediaLibrarySession.Callback.

      Callback kemudian digunakan untuk membuat MediaLibrarySession:

      Kotlin

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

      Java

      mediaLibrarySession =
          new MediaLibrarySession.Builder(context, player, new MySessionCallback()).build();

      Temukan API lengkap MediaLibrarySessionCallback dalam dokumentasi API.

    • Implementasikan MediaSession.Callback.onAddMediaItems(): Callback onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) menyajikan berbagai metode API saat ini dan lama yang menambahkan item media ke pemutar untuk diputar dengan cara yang kompatibel dengan versi lama. Hal ini mencakup metode MediaController.set/addMediaItems() pengontrol Media3, serta metode TransportControls.prepareFrom*/playFrom* API lama. Contoh implementasi callback dapat ditemukan di PlaybackService aplikasi demo sesi.

    • AndroidX Media3 menggunakan androidx.media3.common.MediaItem, bukan MediaBrowserCompat.MediaItem dan MediaMetadataCompat. Bagian kode Anda yang terkait dengan class lama harus diubah sebagaimana mestinya atau dipetakan ke MediaItem Media3.

    • Model pemrograman asinkron umum berubah menjadi Futures, berbeda dengan pendekatan Result yang dapat dilepas dari MediaBrowserServiceCompat. Implementasi layanan Anda dapat menampilkan ListenableFuture asinkron, bukan melepaskan hasil atau menampilkan Future langsung untuk menampilkan nilai secara langsung.

Menghapus PlayerNotificationManager

MediaLibraryService mendukung notifikasi media secara otomatis dan PlayerNotificationManager dapat dihapus saat menggunakan MediaLibraryService atau MediaSessionService.

Aplikasi dapat menyesuaikan notifikasi dengan menetapkan MediaNotification.Provider kustom di onCreate() yang menggantikan DefaultMediaNotificationProvider. MediaLibraryService kemudian akan menangani memulai layanan di latar depan sesuai kebutuhan.

Dengan mengganti MediaLibraryService.updateNotification(), aplikasi dapat mengambil kepemilikan penuh atas postingan notifikasi dan memulai/menghentikan layanan di latar depan sesuai kebutuhan.

Memigrasikan kode klien menggunakan MediaBrowser

Dengan AndroidX Media3, MediaBrowser mengimplementasikan antarmuka MediaController/Player dan dapat digunakan untuk mengontrol pemutaran media selain menjelajahi galeri media. Jika Anda harus membuat MediaBrowserCompat dan MediaControllerCompat di dunia lama, Anda dapat melakukan hal yang sama hanya dengan menggunakan MediaBrowser di Media3.

MediaBrowser dapat dibuat dan menunggu koneksi ke layanan dibuat:

Kotlin

scope.launch {
  val sessionToken = SessionToken(context, ComponentName(context, "MusicService"))
  browser =
    MediaBrowser.Builder(context, sessionToken)
      .setListener(BrowserListener())
      .buildAsync()
      .await()
}

Java

SessionToken sessionToken =
    new SessionToken(context, new ComponentName(context, "MusicService"));
ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken)
        .setListener(new BrowserListener())
        .buildAsync();

Lihat Mengontrol pemutaran di sesi media untuk mempelajari cara membuat MediaController untuk mengontrol pemutaran di latar belakang.

Langkah lebih lanjut dan pembersihan

Error API yang tidak stabil

Setelah bermigrasi ke Media3, Anda mungkin melihat error lint tentang penggunaan API yang tidak stabil. API ini aman digunakan dan error lint adalah produk sampingan dari jaminan kompatibilitas biner baru kami. Jika Anda tidak memerlukan kompatibilitas biner yang ketat, error ini dapat disembunyikan dengan aman menggunakan anotasi @OptIn.

Latar belakang

ExoPlayer v1 atau v2 tidak memberikan jaminan ketat tentang kompatibilitas biner library antara versi berikutnya. Platform ExoPlayer API sengaja dibuat sangat besar agar aplikasi dapat menyesuaikan hampir setiap aspek pemutaran. Versi ExoPlayer berikutnya terkadang akan memperkenalkan penggantian nama simbol atau perubahan yang dapat menyebabkan gangguan lainnya (misalnya, metode baru yang diperlukan pada antarmuka). Dalam sebagian besar kasus, kerusakan ini dimitigasi dengan memperkenalkan simbol baru bersamaan dengan penghentian penggunaan simbol lama untuk beberapa versi, guna memberi developer waktu untuk memigrasikan penggunaannya, tetapi hal ini tidak selalu memungkinkan.

Perubahan yang dapat menyebabkan gangguan ini mengakibatkan dua masalah bagi pengguna library ExoPlayer v1 dan v2:

  1. Upgrade dari versi ExoPlayer dapat menyebabkan kode berhenti mengompilasi.
  2. Aplikasi yang bergantung pada ExoPlayer, baik secara langsung maupun melalui library perantara, harus memastikan bahwa kedua dependensi tersebut memiliki versi yang sama. Jika tidak, ketidaksesuaian biner dapat menyebabkan error runtime.

Peningkatan di Media3

Media3 menjamin kompatibilitas biner untuk subset platform API. Bagian yang tidak menjamin kompatibilitas biner ditandai dengan @UnstableApi. Untuk memperjelas perbedaan ini, penggunaan simbol API yang tidak stabil akan menghasilkan error lint, kecuali jika dianotasi dengan @OptIn.

Setelah bermigrasi dari ExoPlayer v2 ke Media3, Anda mungkin melihat banyak error lint API yang tidak stabil. Hal ini mungkin membuat Media3 tampak 'kurang stabil' dibandingkan ExoPlayer v2. Bukan itu masalahnya. Bagian 'tidak stabil' dari Media3 API memiliki tingkat stabilitas yang sama dengan seluruh platform ExoPlayer v2 API, dan jaminan platform Media3 API yang stabil tidak tersedia di ExoPlayer v2 sama sekali. Perbedaannya adalah error lint kini memberi tahu Anda tentang berbagai tingkat stabilitas.

Menangani error lint API yang tidak stabil

Lihat bagian pemecahan masalah pada error lint ini untuk mengetahui detail tentang cara memberi anotasi pada penggunaan Java dan Kotlin API yang tidak stabil dengan @OptIn.

API tidak digunakan lagi

Anda mungkin melihat bahwa panggilan ke API yang tidak digunakan lagi dicoret di Android Studio. Sebaiknya ganti panggilan tersebut dengan alternatif yang sesuai. Arahkan kursor ke simbol untuk melihat JavaDoc yang memberi tahu API mana yang harus digunakan.

Screenshot: Cara menampilkan JavaDoc dengan alternatif metode yang tidak digunakan lagi
Gambar 3: Tooltip JavaDoc di Android Studio menyarankan alternatif untuk simbol yang tidak digunakan lagi.

Contoh kode dan aplikasi demo