ExoPlayer menyediakan fungsi untuk mendownload media untuk pemutaran offline. Dalam sebagian besar
kasus penggunaan, sebaiknya download dilanjutkan meskipun aplikasi Anda berada di
latar belakang. Untuk kasus penggunaan ini, aplikasi Anda harus membuat subclass DownloadService dan
mengirim perintah ke layanan untuk menambahkan, menghapus, dan mengontrol download. Diagram
berikut menunjukkan class utama yang terlibat.
DownloadService: MembungkusDownloadManagerdan meneruskan perintah keDownloadManagertersebut. Layanan ini memungkinkanDownloadManagertetap berjalan meskipun aplikasi berada di latar belakang.DownloadManager: Mengelola beberapa download, memuat (dan menyimpan) statusnya dari (dan ke)DownloadIndex, memulai dan menghentikan download berdasarkan persyaratan seperti konektivitas jaringan, dan sebagainya. Untuk mendownload konten, pengelola biasanya akan membaca data yang didownload dariHttpDataSource, dan menuliskannya keCache.DownloadIndex: Mempertahankan status download.
Membuat DownloadService
Untuk membuat DownloadService, buat subclass dan implementasikan metode abstraknya:
getDownloadManager(): MenampilkanDownloadManageryang akan digunakan.getScheduler(): MenampilkanScheduleropsional, yang dapat memulai ulang layanan saat persyaratan yang diperlukan untuk melanjutkan download yang tertunda terpenuhi. ExoPlayer menyediakan implementasi berikut:PlatformScheduler, yang menggunakan JobScheduler (API Minimum adalah 21). Lihat javadoc PlatformScheduler untuk persyaratan izin aplikasi.WorkManagerScheduler, yang menggunakan WorkManager.
getForegroundNotification(): Menampilkan notifikasi yang akan ditampilkan saat layanan berjalan di latar depan. Anda dapat menggunakanDownloadNotificationHelper.buildProgressNotificationuntuk membuat notifikasi dalam gaya default.
Terakhir, tentukan layanan di file AndroidManifest.xml Anda:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<application>
<service android:name="com.myapp.MyDownloadService"
android:exported="false"
android:foregroundServiceType="dataSync">
<!-- This is needed for Scheduler -->
<intent-filter>
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
</application>
Lihat DemoDownloadService dan AndroidManifest.xml di aplikasi demo ExoPlayer untuk mengetahui contoh konkretnya.
Membuat DownloadManager
Cuplikan kode berikut menunjukkan cara membuat instance DownloadManager, yang dapat ditampilkan oleh getDownloadManager() di DownloadService Anda:
Kotlin
// Note: This should be a singleton in your app. val databaseProvider = StandaloneDatabaseProvider(context) // A download cache should not evict media, so should use a NoopCacheEvictor. val downloadCache = SimpleCache(downloadDirectory, NoOpCacheEvictor(), databaseProvider) // Create a factory for reading the data from the network. val dataSourceFactory = DefaultHttpDataSource.Factory() // Choose an executor for downloading data. Using Runnable::run will cause each download task to // download data on its own thread. Passing an executor that uses multiple threads will speed up // download tasks that can be split into smaller parts for parallel execution. Applications that // already have an executor for background downloads may wish to reuse their existing executor. val downloadExecutor = Executor(Runnable::run) // Create the download manager. val downloadManager = DownloadManager(context, databaseProvider, downloadCache, dataSourceFactory, downloadExecutor) // Optionally, properties can be assigned to configure the download manager. downloadManager.requirements = requirements downloadManager.maxParallelDownloads = 3
Java
// Note: This should be a singleton in your app. databaseProvider = new StandaloneDatabaseProvider(context); // A download cache should not evict media, so should use a NoopCacheEvictor. downloadCache = new SimpleCache(downloadDirectory, new NoOpCacheEvictor(), databaseProvider); // Create a factory for reading the data from the network. dataSourceFactory = new DefaultHttpDataSource.Factory(); // Choose an executor for downloading data. Using Runnable::run will cause each download task to // download data on its own thread. Passing an executor that uses multiple threads will speed up // download tasks that can be split into smaller parts for parallel execution. Applications that // already have an executor for background downloads may wish to reuse their existing executor. Executor downloadExecutor = Runnable::run; // Create the download manager. downloadManager = new DownloadManager( context, databaseProvider, downloadCache, dataSourceFactory, downloadExecutor); // Optionally, setters can be called to configure the download manager. downloadManager.setRequirements(requirements); downloadManager.setMaxParallelDownloads(3);
Lihat DemoUtil di aplikasi demo untuk mengetahui contoh konkretnya.
Menambahkan download
Untuk menambahkan download, buat DownloadRequest dan kirimkan ke
DownloadService. Untuk streaming adaptif, gunakan DownloadHelper untuk membantu
membangun DownloadRequest. Contoh
berikut menunjukkan cara membuat permintaan download:
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
Dalam contoh ini, contentId adalah ID unik untuk konten. Dalam kasus sederhana, contentUri sering kali dapat digunakan sebagai contentId, tetapi aplikasi bebas menggunakan skema ID apa pun yang paling sesuai dengan kasus penggunaannya. DownloadRequest.Builder juga memiliki beberapa setter opsional. Misalnya, setKeySetId dan setData dapat digunakan untuk
menetapkan DRM dan data kustom yang ingin dikaitkan aplikasi dengan download,
masing-masing. Jenis MIME konten juga dapat ditentukan menggunakan setMimeType,
sebagai petunjuk untuk kasus ketika jenis konten tidak dapat disimpulkan dari contentUri.
Setelah dibuat, permintaan dapat dikirim ke DownloadService untuk menambahkan
download:
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false, )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
Dalam contoh ini, MyDownloadService adalah subclass DownloadService aplikasi, dan
parameter foreground mengontrol apakah layanan akan dimulai di
latar depan. Jika aplikasi Anda sudah berada di latar depan, parameter foreground
biasanya harus disetel ke false karena DownloadService akan
menempatkan dirinya di latar depan jika menentukan bahwa ada tugas yang harus dilakukan.
Menghapus download
Download dapat dihapus dengan mengirim perintah hapus ke DownloadService,
dengan contentId mengidentifikasi download yang akan dihapus:
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false, )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
Anda juga dapat menghapus semua data yang didownload dengan
DownloadService.sendRemoveAllDownloads.
Memulai dan menghentikan download
Download hanya akan berjalan jika empat kondisi terpenuhi:
- Download tidak memiliki alasan penghentian.
- Download tidak dijeda.
- Persyaratan agar download dapat dilanjutkan terpenuhi. Persyaratan dapat menentukan batasan pada jenis jaringan yang diizinkan, serta apakah perangkat harus dalam kondisi tidak ada aktivitas atau terhubung ke pengisi daya.
- Jumlah download paralel maksimum tidak terlampaui.
Semua kondisi ini dapat dikontrol dengan mengirim perintah ke
DownloadService.
Menetapkan dan menghapus alasan penghentian download
Anda dapat menetapkan alasan penghentian satu atau semua download:
Kotlin
// Set the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService::class.java, contentId, stopReason, /* foreground= */ false, ) // Clear the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService::class.java, contentId, Download.STOP_REASON_NONE, /* foreground= */ false, )
Java
// Set the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService.class, contentId, stopReason, /* foreground= */ false); // Clear the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService.class, contentId, Download.STOP_REASON_NONE, /* foreground= */ false);
stopReason dapat berupa nilai non-nol (Download.STOP_REASON_NONE = 0 adalah
nilai khusus yang berarti bahwa download tidak dihentikan). Aplikasi yang memiliki
beberapa alasan untuk menghentikan download dapat menggunakan nilai yang berbeda untuk melacak
alasan setiap download dihentikan. Menetapkan dan menghapus alasan penghentian untuk semua
download berfungsi sama seperti menetapkan dan menghapus alasan penghentian untuk
satu download, kecuali contentId harus ditetapkan ke null.
Jika download memiliki alasan penghentian yang bukan nol, download akan berada dalam status
Download.STATE_STOPPED. Alasan penghentian dipertahankan di
DownloadIndex, dan akan tetap ada jika proses aplikasi dihentikan dan
dimulai ulang.
Menjeda dan melanjutkan semua download
Semua download dapat dijeda dan dilanjutkan sebagai berikut:
Kotlin
// Pause all downloads. DownloadService.sendPauseDownloads( context, MyDownloadService::class.java, /* foreground= */ false, ) // Resume all downloads. DownloadService.sendResumeDownloads( context, MyDownloadService::class.java, /* foreground= */ false, )
Java
// Pause all downloads. DownloadService.sendPauseDownloads(context, MyDownloadService.class, /* foreground= */ false); // Resume all downloads. DownloadService.sendResumeDownloads(context, MyDownloadService.class, /* foreground= */ false);
Saat dijeda, download akan berada dalam status Download.STATE_QUEUED.
Tidak seperti menetapkan alasan penghentian, pendekatan ini tidak mempertahankan perubahan status apa pun. Hal ini hanya memengaruhi status runtime DownloadManager.
Menetapkan persyaratan agar download dapat dilanjutkan
Requirements dapat digunakan untuk menentukan batasan yang harus dipenuhi agar
download dapat dilanjutkan. Persyaratan dapat ditetapkan dengan memanggil
DownloadManager.setRequirements() saat membuat DownloadManager, seperti pada
contoh di atas. Nilai ini juga dapat diubah secara dinamis dengan mengirim perintah
ke DownloadService:
Kotlin
// Set the download requirements. DownloadService.sendSetRequirements( context, MyDownloadService::class.java, requirements, /* foreground= */ false, )
Java
// Set the download requirements. DownloadService.sendSetRequirements( context, MyDownloadService.class, requirements, /* foreground= */ false);
Jika download tidak dapat dilanjutkan karena persyaratan tidak terpenuhi, download akan berada dalam status Download.STATE_QUEUED. Anda dapat membuat kueri persyaratan yang tidak terpenuhi dengan DownloadManager.getNotMetRequirements().
Menetapkan jumlah maksimum download paralel
Jumlah maksimum download paralel dapat ditetapkan dengan memanggil
DownloadManager.setMaxParallelDownloads(). Hal ini biasanya dilakukan saat
membuat DownloadManager, seperti dalam contoh di atas.
Jika download tidak dapat dilanjutkan karena jumlah maksimum download paralel telah
berlangsung, download akan berada dalam status Download.STATE_QUEUED.
Membuat kueri download
DownloadIndex DownloadManager dapat dikueri untuk mengetahui status semua
download, termasuk yang telah selesai atau gagal. DownloadIndex
dapat diperoleh dengan memanggil DownloadManager.getDownloadIndex(). Kursor yang mengulangi semua download kemudian dapat diperoleh dengan memanggil DownloadIndex.getDownloads(). Atau, status satu download
dapat dikueri dengan memanggil DownloadIndex.getDownload().
DownloadManager juga menyediakan DownloadManager.getCurrentDownloads(), yang hanya menampilkan status download saat ini (yaitu, belum selesai atau gagal). Metode
ini berguna untuk memperbarui notifikasi dan komponen UI lainnya yang menampilkan
progres dan status download saat ini.
Mendengarkan hasil download
Anda dapat menambahkan pemroses ke DownloadManager untuk mengetahui saat status download saat ini berubah:
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
Lihat DownloadManagerListener di class DownloadTracker aplikasi demo untuk
contoh konkret.
Memutar konten yang didownload
Memutar konten yang didownload mirip dengan memutar konten online, kecuali data dibaca dari download Cache, bukan melalui jaringan.
Untuk memutar konten yang didownload, buat CacheDataSource.Factory menggunakan instance
Cache yang sama yang digunakan untuk mendownload, dan masukkan ke
DefaultMediaSourceFactory saat membuat pemutar:
Kotlin
// Create a read-only cache data source factory using the download cache. val cacheDataSourceFactory: DataSource.Factory = CacheDataSource.Factory() .setCache(downloadCache) .setUpstreamDataSourceFactory(httpDataSourceFactory) .setCacheWriteDataSinkFactory(null) // Disable writing. val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory) ) .build()
Java
// Create a read-only cache data source factory using the download cache. DataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory() .setCache(downloadCache) .setUpstreamDataSourceFactory(httpDataSourceFactory) .setCacheWriteDataSinkFactory(null); // Disable writing. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory)) .build();
Jika instance pemutar yang sama juga akan digunakan untuk memutar konten yang tidak didownload, CacheDataSource.Factory harus dikonfigurasi sebagai hanya baca untuk menghindari konten tersebut didownload juga selama pemutaran.
Setelah dikonfigurasi dengan CacheDataSource.Factory, pemutar akan
memiliki akses ke konten yang didownload untuk diputar. Memutar hasil download kemudian semudah meneruskan MediaItem yang sesuai ke pemutar. MediaItem
dapat diperoleh dari Download menggunakan Download.request.toMediaItem, atau
langsung dari DownloadRequest menggunakan DownloadRequest.toMediaItem.
Konfigurasi MediaSource
Contoh sebelumnya membuat cache download tersedia untuk pemutaran semua
MediaItem. Anda juga dapat menyediakan cache download untuk
instance MediaSource individual, yang dapat diteruskan langsung ke pemutar:
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)) player.setMediaSource(mediaSource) player.prepare()
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)); player.setMediaSource(mediaSource); player.prepare();
Mendownload dan memutar streaming adaptif
Streaming adaptif (misalnya, DASH, SmoothStreaming, dan HLS) biasanya berisi beberapa jalur media. Sering kali ada beberapa trek yang berisi konten yang sama dalam kualitas yang berbeda (misalnya, trek video SD, HD, dan 4K). Mungkin juga ada beberapa jalur jenis yang sama yang berisi konten berbeda (misalnya, beberapa jalur audio dalam bahasa yang berbeda).
Untuk pemutaran streaming, pemilih trek dapat digunakan untuk memilih trek mana yang diputar. Demikian pula, untuk mendownload, DownloadHelper dapat digunakan untuk memilih trek mana yang didownload. Penggunaan DownloadHelper yang umum mengikuti langkah-langkah berikut:
- Bangun
DownloadHelpermenggunakan instanceDownloadHelper.Factory. Siapkan pembantu dan tunggu callback. - Jika perlu, periksa trek yang dipilih secara default menggunakan
getMappedTrackInfodangetTrackSelections, lalu sesuaikan menggunakanclearTrackSelections,replaceTrackSelections, danaddTrackSelection. - Buat
DownloadRequestuntuk trek yang dipilih dengan memanggilgetDownloadRequest. Permintaan dapat diteruskan keDownloadServiceAnda untuk menambahkan download, seperti yang dijelaskan di atas. - Lepaskan helper menggunakan
release().
Kotlin
val downloadHelper = DownloadHelper.Factory() .setRenderersFactory(DefaultRenderersFactory(context)) .setDataSourceFactory(dataSourceFactory) .create(MediaItem.fromUri(contentUri)) downloadHelper.prepare(callback)
Java
DownloadHelper downloadHelper = new DownloadHelper.Factory() .setRenderersFactory(new DefaultRenderersFactory(context)) .setDataSourceFactory(dataSourceFactory) .create(MediaItem.fromUri(contentUri)); downloadHelper.prepare(callback);
Pemutaran konten adaptif yang didownload memerlukan konfigurasi pemutar dan
penerusan MediaItem yang sesuai, seperti yang dijelaskan di atas.
Saat membuat MediaItem, MediaItem.localConfiguration.streamKeys harus
ditetapkan agar cocok dengan yang ada di DownloadRequest sehingga pemutar hanya mencoba
memutar subkumpulan trek yang telah didownload. Menggunakan
Download.request.toMediaItem dan DownloadRequest.toMediaItem untuk membangun
MediaItem akan menanganinya untuk Anda.