ExoPlayer menyediakan fungsionalitas untuk mendownload media untuk pemutaran offline. Dalam sebagian besar
sebaiknya download terus berlanjut bahkan saat aplikasi Anda berada dalam
latar belakang. Untuk kasus penggunaan ini, aplikasi Anda harus membuat subclass DownloadService
dan
mengirimkan perintah ke layanan untuk menambahkan, menghapus, dan mengontrol download. Tujuan
diagram berikut menunjukkan class utama yang terlibat.
DownloadService
: MenggabungkanDownloadManager
dan meneruskan perintah ke sana. Tujuan memungkinkanDownloadManager
untuk tetap berjalan bahkan saat aplikasi berada dalam latar belakang.DownloadManager
: Mengelola beberapa download, memuat (dan menyimpan) status dari (dan ke)DownloadIndex
, yang memulai dan menghentikan download berdasarkan sesuai kebutuhan, misalnya konektivitas jaringan, dan sebagainya. Untuk mendownload konten, pengelola biasanya akan membaca data yang diunduh dariHttpDataSource
, lalu tulis keCache
.DownloadIndex
: Mempertahankan status download.
Membuat DownloadService
Untuk membuat DownloadService
, buat subclass dan implementasikan
metode abstrak:
getDownloadManager()
: MenampilkanDownloadManager
yang akan digunakan.getScheduler()
: MenampilkanScheduler
opsional, yang dapat memulai ulang metode layanan saat persyaratan yang diperlukan agar progres download yang tertunda terpenuhi. ExoPlayer menyediakan implementasi ini:PlatformScheduler
, yang menggunakan JobScheduler (Minimum API adalah 21). Lihat javadocs PlatformScheduler untuk persyaratan izin aplikasi.WorkManagerScheduler
, yang menggunakan WorkManager.
getForegroundNotification()
: Menampilkan notifikasi yang akan ditampilkan saat layanan berjalan di latar depan. Anda dapat menggunakanDownloadNotificationHelper.buildProgressNotification
untuk 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>
Melihat DemoDownloadService
dan AndroidManifest.xml
di ExoPlayer
aplikasi demo untuk contoh konkret.
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 contoh konkret.
Menambahkan download
Untuk menambahkan download, buat DownloadRequest
dan kirimkan ke
DownloadService
. Untuk streaming adaptif, gunakan DownloadHelper
untuk membantu
membuat DownloadRequest
. Hal berikut
contoh 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 dapat digunakan secara gratis
skema ID apa pun yang paling sesuai
dengan kasus penggunaan mereka. DownloadRequest.Builder
juga memiliki
beberapa penyetel opsional. Misalnya, setKeySetId
dan setData
dapat digunakan untuk
menyetel DRM dan data khusus yang ingin dikaitkan oleh aplikasi dengan download,
secara berurutan. Jenis MIME konten juga dapat ditetapkan menggunakan setMimeType
,
sebagai petunjuk untuk kasus saat jenis konten tidak dapat disimpulkan dari contentUri
.
Setelah dibuat, permintaan dapat dikirim ke DownloadService
untuk menambahkan
unduh:
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 ada di latar depan, foreground
biasanya harus disetel ke false
karena DownloadService
akan
menempatkan dirinya di latar depan jika ia menentukan
bahwa ia memiliki pekerjaan yang harus dilakukan.
Menghapus download
Download dapat dihapus dengan mengirimkan perintah {i>remove<i} 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 diunduh menggunakan
DownloadService.sendRemoveAllDownloads
.
Memulai dan menghentikan download
Download hanya akan berlangsung jika empat kondisi terpenuhi:
- Download tidak memiliki alasan untuk berhenti.
- Download tidak dijeda.
- Persyaratan download untuk progres terpenuhi. Persyaratan dapat menentukan pada jenis jaringan yang diizinkan, serta apakah perangkat tidak ada aktivitas atau tersambung ke pengisi daya.
- Jumlah maksimum download paralel tidak terlampaui.
Semua kondisi ini dapat dikontrol dengan mengirimkan perintah ke
DownloadService
.
Menyetel dan menghapus alasan penghentian download
Anda dapat menetapkan alasan untuk satu atau semua download dihentikan:
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 apa pun yang bukan 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 bekerja dengan cara yang sama seperti menyetel dan menghapus alasan penghentian untuk
download tunggal, kecuali bahwa contentId
harus disetel ke null
.
Ketika download memiliki alasan yang bukan
nol berhenti, itu akan berada di
status Download.STATE_STOPPED
. Alasan penghentian disimpan di
DownloadIndex
, sehingga dipertahankan jika proses aplikasi dihentikan dan
kemudian 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 status apa pun
perubahan. Hal ini hanya memengaruhi status runtime DownloadManager
.
Menetapkan persyaratan untuk melanjutkan download
Requirements
dapat digunakan untuk menentukan batasan yang harus dipenuhi untuk
unduhan untuk melanjutkan. Persyaratannya bisa diatur
dengan memanggil
DownloadManager.setRequirements()
saat membuat DownloadManager
, seperti dalam
contoh di atas. Nama itu juga dapat diubah secara dinamis dengan mengirimkan 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 mengkueri data tidak terpenuhi
persyaratan dengan DownloadManager.getNotMetRequirements()
.
Menetapkan jumlah maksimum download paralel
Jumlah maksimum download paralel dapat ditetapkan dengan memanggil
DownloadManager.setMaxParallelDownloads()
. Ini biasanya akan
dilakukan ketika
membuat DownloadManager
, seperti pada contoh di atas.
Saat download tidak dapat dilanjutkan karena jumlah maksimum download paralel
sedang berlangsung, serta akan berada dalam status Download.STATE_QUEUED
.
Membuat kueri download
DownloadIndex
dari DownloadManager
dapat dikueri status semua
download, termasuk download yang telah selesai atau gagal. DownloadIndex
dapat diperoleh dengan memanggil DownloadManager.getDownloadIndex()
. Kursor yang
melakukan iterasi pada semua download kemudian
dapat diperoleh dengan memanggil
DownloadIndex.getDownloads()
. Atau, status download tunggal
dapat dikueri dengan memanggil DownloadIndex.getDownload()
.
DownloadManager
juga menyediakan DownloadManager.getCurrentDownloads()
, yang
mengembalikan status download saat ini saja (yaitu tidak selesai atau gagal). Ini
berguna untuk memperbarui notifikasi dan komponen UI lain yang menampilkan
kemajuan dan status unduhan saat ini.
Mendengarkan download
Anda dapat menambahkan pemroses ke DownloadManager
untuk diberi tahu saat pemroses
status perubahan download:
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 mengetahui
sebuah contoh konkret.
Memutar konten yang didownload
Memutar konten yang didownload mirip dengan memutar konten online, hanya saja
data dibaca dari download Cache
, bukan melalui jaringan.
Untuk memutar konten yang didownload, buat CacheDataSource.Factory
menggunakan
Instance Cache
yang digunakan untuk mendownload, dan memasukkannya ke dalam
DefaultMediaSourceFactory
saat membangun pemain:
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
maka CacheDataSource.Factory
harus dikonfigurasi
sebagai hanya-baca untuk menghindari
mengunduh konten itu juga selama pemutaran.
Setelah pemutar dikonfigurasi dengan CacheDataSource.Factory
, pemutar tersebut akan
memiliki akses ke konten yang didownload untuk pemutaran. Memutar 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
dtk. Anda juga dapat membuat {i>
cache<i} download tersedia 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 trek media. Sering kali ada beberapa lagu yang berisi konten yang sama di kualitas yang berbeda (misalnya trek video SD, HD, dan 4K). Mungkin juga ada beberapa trek dari jenis yang sama yang berisi konten berbeda (mis. beberapa trek audio dalam berbagai bahasa).
Untuk pemutaran streaming, pemilih trek dapat digunakan untuk memilih trek
lagu diputar. Demikian pula, untuk mendownload, DownloadHelper
dapat digunakan untuk
memilih lagu yang akan didownload. Penggunaan standar DownloadHelper
mengikuti langkah-langkah berikut:
- Membangun
DownloadHelper
menggunakan salah satuDownloadHelper.forMediaItem
metode. Siapkan helper dan tunggu callback.Kotlin
val downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), DefaultRenderersFactory(context), dataSourceFactory ) downloadHelper.prepare(callback)
Java
DownloadHelper downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), new DefaultRenderersFactory(context), dataSourceFactory); downloadHelper.prepare(callback);
- Anda juga dapat memeriksa trek default yang dipilih menggunakan
getMappedTrackInfo
dangetTrackSelections
, serta melakukan penyesuaian menggunakanclearTrackSelections
,replaceTrackSelections
danaddTrackSelection
. - Buat
DownloadRequest
untuk trek yang dipilih dengan memanggilgetDownloadRequest
. Permintaan dapat diteruskan keDownloadService
Anda untuk tambahkan download, seperti dijelaskan di atas. - Rilis helper menggunakan
release()
.
Pemutaran konten adaptif yang didownload memerlukan konfigurasi pemutar dan
meneruskan MediaItem
yang sesuai, seperti yang dijelaskan di atas.
Saat membuat MediaItem
, MediaItem.localConfiguration.streamKeys
harus
diatur agar sesuai dengan yang ada di DownloadRequest
sehingga pemain hanya mencoba
memutar sebagian lagu yang telah didownload. Menggunakan
Download.request.toMediaItem
dan DownloadRequest.toMediaItem
untuk membuat
MediaItem
akan menanganinya untuk Anda.