ExoPlayer cung cấp chức năng tải nội dung nghe nhìn xuống để phát khi không có mạng. Trong hầu hết
trong trường hợp sử dụng, bạn nên tiếp tục tải xuống ngay cả khi ứng dụng đang ở
nền. Đối với những trường hợp sử dụng này, ứng dụng nên phân lớp con DownloadService
và
gửi lệnh đến dịch vụ để thêm, xoá và kiểm soát tệp đã tải xuống. Chiến lược phát hành đĩa đơn
sơ đồ dưới đây cho thấy các lớp chính có tham gia.
DownloadService
: Gói mộtDownloadManager
và chuyển tiếp các lệnh đến đó. Chiến lược phát hành đĩa đơn cho phépDownloadManager
tiếp tục chạy ngay cả khi ứng dụng đang ở nền.DownloadManager
: Quản lý nhiều tệp đã tải xuống, tải (và lưu trữ) các tệp trạng thái từ (và đến)DownloadIndex
, bắt đầu và dừng tải xuống dựa trên dựa trên các yêu cầu như kết nối mạng, v.v. Để tải xuống thì trình quản lý thường sẽ đọc dữ liệu được tải xuống từHttpDataSource
rồi ghi mã này vàoCache
.DownloadIndex
: Duy trì trạng thái của tệp đã tải xuống.
Tạo DownloadService
Để tạo một DownloadService
, hãy tạo lớp con và triển khai lớp này
phương thức trừu tượng:
getDownloadManager()
: Trả vềDownloadManager
sẽ được sử dụng.getScheduler()
: Trả về mộtScheduler
không bắt buộc, có thể khởi động lại khi đáp ứng các yêu cầu cần thiết về quá trình tải xuống đang chờ xử lý. ExoPlayer cung cấp những cách triển khai sau:PlatformScheduler
, sử dụng jobScheduler (API tối thiểu là 21). Xem javadocs PlatformScheduler để biết các yêu cầu về quyền cho ứng dụng.WorkManagerScheduler
sử dụng WorkManager.
getForegroundNotification()
: Trả về một thông báo sẽ được hiển thị khi đang chạy trên nền trước. Bạn có thể sử dụngDownloadNotificationHelper.buildProgressNotification
để tạo một theo kiểu mặc định.
Cuối cùng, hãy xác định dịch vụ trong tệp AndroidManifest.xml
:
<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>
Xem DemoDownloadService
và AndroidManifest.xml
trong ExoPlayer
ứng dụng minh hoạ để có một ví dụ cụ thể.
Tạo DownloadManager
Đoạn mã sau đây minh hoạ cách tạo DownloadManager
,
có thể được getDownloadManager()
trả về trong DownloadService
của bạn:
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);
Hãy xem DemoUtil
trong ứng dụng minh hoạ để biết ví dụ cụ thể.
Thêm một tệp tải xuống
Để thêm một tệp tải xuống, hãy tạo một DownloadRequest
rồi gửi tệp đó đến
DownloadService
Đối với luồng thích ứng, hãy sử dụng DownloadHelper
để giúp
tạo DownloadRequest
. Nội dung sau đây
ví dụ cho thấy cách tạo một yêu cầu tải xuống:
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
Trong ví dụ này, contentId
là giá trị nhận dạng duy nhất cho nội dung. Trong những trường hợp đơn giản,
contentUri
thường có thể được dùng làm contentId
. Tuy nhiên, các ứng dụng miễn phí sử dụng
bất kỳ lược đồ ID nào phù hợp nhất với trường hợp sử dụng của họ. DownloadRequest.Builder
cũng có
một số phương thức setter tuỳ chọn. Ví dụ: bạn có thể dùng setKeySetId
và setData
để
đặt DRM và dữ liệu tuỳ chỉnh mà ứng dụng muốn liên kết với nội dung tải xuống,
. Bạn cũng có thể chỉ định loại MIME của nội dung bằng setMimeType
,
là gợi ý cho trường hợp không thể suy ra loại nội dung từ contentUri
.
Sau khi tạo xong, bạn có thể gửi yêu cầu tới DownloadService
để thêm
tải xuống:
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
Trong ví dụ này, MyDownloadService
là lớp con DownloadService
của ứng dụng và
Tham số foreground
kiểm soát việc dịch vụ có bắt đầu trong
nền trước. Nếu ứng dụng của bạn đã chạy ở nền trước, thì foreground
tham số thường phải được đặt thành false
, vì DownloadService
sẽ
tự đặt nó ở nền trước nếu xác định rằng nó còn nhiều việc cần làm.
Đang xóa các mục đã tải xuống
Bạn có thể xoá một tệp đã tải xuống bằng cách gửi lệnh xoá tới DownloadService
,
trong đó contentId
xác định tệp tải xuống sẽ bị xoá:
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
Bạn cũng có thể xoá tất cả dữ liệu đã tải xuống bằng
DownloadService.sendRemoveAllDownloads
.
Bắt đầu và dừng tải xuống
Quá trình tải xuống sẽ chỉ diễn ra nếu đáp ứng 4 điều kiện:
- Không có lý do dừng để tải xuống.
- Quá trình tải xuống không bị tạm dừng.
- Yêu cầu đối với quá trình tải xuống được đáp ứng. Các yêu cầu có thể chỉ định các hạn chế đối với các loại mạng được phép cũng như liệu thiết bị có nên ở trạng thái rảnh hoặc kết nối với bộ sạc.
- Không vượt quá số lượng tối đa lượt tải xuống song song.
Bạn có thể kiểm soát tất cả các điều kiện này bằng cách gửi lệnh đến
DownloadService
.
Thiết lập và xoá lý do ngừng tải xuống
Bạn có thể đặt lý do dừng một hoặc tất cả nội dung tải xuống:
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
có thể là bất kỳ giá trị nào khác 0 (Download.STOP_REASON_NONE = 0
là
một giá trị đặc biệt, nghĩa là quá trình tải xuống sẽ không bị dừng lại). Ứng dụng có
nhiều lý do khiến bạn dừng tải xuống có thể dùng các giá trị khác nhau để tiếp tục theo dõi
về lý do khiến mỗi quá trình tải xuống bị dừng. Đặt và xoá lý do ngừng cho tất cả
tải xuống hoạt động giống như đặt và xoá lý do ngừng cho
một lượt tải xuống, ngoại trừ việc contentId
phải được đặt thành null
.
Khi tệp tải xuống có lý do dừng khác 0, tệp đó sẽ nằm trong
Trạng thái Download.STATE_STOPPED
. Lý do dừng vẫn tồn tại trong
DownloadIndex
, và vì vậy sẽ được giữ lại nếu quy trình đăng ký bị dừng và
khởi động lại sau đó.
Đang tạm dừng và tiếp tục tất cả nội dung tải xuống
Bạn có thể tạm dừng và tiếp tục tất cả nội dung tải xuống như sau:
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);
Khi tải xuống bị tạm dừng, các tệp sẽ ở trạng thái Download.STATE_QUEUED
.
Không giống như đặt lý do dừng, phương pháp này không duy trì bất kỳ trạng thái nào
thay đổi. Việc này chỉ ảnh hưởng đến trạng thái thời gian chạy của DownloadManager
.
Đặt yêu cầu để tải xuống
Bạn có thể sử dụng Requirements
để chỉ định các quy tắc ràng buộc phải được đáp ứng cho
tải xuống để tiếp tục. Bạn có thể đặt các yêu cầu này bằng cách gọi
DownloadManager.setRequirements()
khi tạo DownloadManager
, như trong
ví dụ ở trên. Bạn cũng có thể thay đổi các mã này một cách linh động bằng cách gửi một lệnh
vào 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);
Khi quá trình tải xuống không thể tiếp tục do không đáp ứng các yêu cầu,
sẽ ở trạng thái Download.STATE_QUEUED
. Bạn có thể truy vấn cụm từ không được đáp ứng
với DownloadManager.getNotMetRequirements()
.
Đặt số lượng tối đa lượt tải xuống song song
Bạn có thể đặt số lượng tối đa các tệp tải xuống song song bằng cách gọi
DownloadManager.setMaxParallelDownloads()
. Điều này thường được thực hiện khi
tạo DownloadManager
, như trong ví dụ ở trên.
Khi quá trình tải xuống không tiếp tục được thực hiện do đã đạt số lượng tối đa cho phép tải xuống song song
đang diễn ra, mục này sẽ ở trạng thái Download.STATE_QUEUED
.
Truy vấn tệp đã tải xuống
Bạn có thể truy vấn DownloadIndex
của DownloadManager
để biết trạng thái của tất cả
tệp tải xuống, bao gồm cả những tệp đã hoàn tất hoặc không thành công. DownloadIndex
có thể được lấy bằng cách gọi DownloadManager.getDownloadIndex()
. Con trỏ
lặp lại tất cả các lần tải xuống sau đó có thể được lấy bằng cách gọi
DownloadIndex.getDownloads()
. Ngoài ra, trạng thái của một lượt tải xuống
có thể được truy vấn bằng cách gọi DownloadIndex.getDownload()
.
DownloadManager
cũng cung cấp DownloadManager.getCurrentDownloads()
,
chỉ trả về trạng thái của tệp tải xuống hiện tại (tức là chưa hoàn tất hoặc không thành công). Chiến dịch này
rất hữu ích trong việc cập nhật thông báo và các thành phần giao diện người dùng khác hiển thị
tiến trình và trạng thái của các lượt tải xuống hiện tại.
Nghe nội dung đã tải xuống
Bạn có thể thêm một trình nghe vào DownloadManager
để nhận thông báo khi
trạng thái thay đổi của tệp tải xuống:
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
Xem DownloadManagerListener
trong lớp DownloadTracker
của ứng dụng minh hoạ để biết
một ví dụ cụ thể.
Đang phát nội dung đã tải xuống
Phát nội dung đã tải xuống cũng tương tự như phát nội dung trực tuyến, ngoại trừ
thì dữ liệu sẽ được đọc từ tệp tải xuống Cache
thay vì qua mạng.
Để phát nội dung đã tải xuống, hãy tạo một CacheDataSource.Factory
bằng cùng một
Thực thể Cache
được dùng để tải xuống rồi chèn vào thực thể đó
DefaultMediaSourceFactory
khi tạo trình phát:
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();
Nếu cùng một phiên bản trình phát cũng được dùng để phát nội dung không được tải xuống
thì bạn nên định cấu hình CacheDataSource.Factory
thành chỉ đọc để tránh
tải nội dung đó xuống trong lúc phát.
Sau khi được định cấu hình bằng CacheDataSource.Factory
, trình phát sẽ
có quyền truy cập vào nội dung đã tải xuống để phát lại. Sau đó, phát nội dung tải xuống
chỉ đơn giản như truyền MediaItem
tương ứng đến người chơi. Một MediaItem
có thể lấy được từ Download
bằng Download.request.toMediaItem
, hoặc
trực tiếp từ DownloadRequest
bằng DownloadRequest.toMediaItem
.
Cấu hình MediaSource
Ví dụ trước cung cấp bộ nhớ đệm tải xuống để phát lại tất cả
MediaItem
giây. Bạn cũng có thể cung cấp bộ nhớ đệm tải xuống cho
thực thể MediaSource
riêng lẻ có thể được truyền trực tiếp đến người chơi:
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();
Tải và phát luồng thích ứng
Luồng thích ứng (ví dụ: DASH, suốt 64 ngày và HLS) thường chứa nhiều bản nhạc đa phương tiện. Thường có nhiều bản nhạc chứa cùng một nội dung trong có chất lượng khác nhau (ví dụ: bản video SD, HD và 4K). Cũng có thể có nhiều bản nhạc cùng loại chứa nội dung khác nhau (ví dụ: nhiều bản nhạc bản âm thanh bằng nhiều ngôn ngữ).
Đối với các lần phát trực tuyến, bạn có thể sử dụng bộ chọn bản nhạc để chọn
bản nhạc được phát. Tương tự, để tải xuống, bạn có thể sử dụng DownloadHelper
để
chọn bản nhạc để tải xuống. Cách sử dụng thông thường của DownloadHelper
hãy làm theo các bước sau:
- Tạo
DownloadHelper
bằng một trongDownloadHelper.forMediaItem
. Chuẩn bị trình trợ giúp và chờ lệnh gọi lại.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);
- Kiểm tra các kênh mặc định được chọn bằng cách sử dụng
getMappedTrackInfo
(không bắt buộc) vàgetTrackSelections
, đồng thời điều chỉnh bằngclearTrackSelections
,replaceTrackSelections
vàaddTrackSelection
. - Tạo
DownloadRequest
cho các bản nhạc đã chọn bằng cách gọigetDownloadRequest
Yêu cầu có thể được chuyển đếnDownloadService
của bạn tới thêm tệp tải xuống như đã mô tả ở trên. - Phát hành trình trợ giúp bằng cách sử dụng
release()
.
Để phát nội dung thích ứng đã tải xuống, bạn phải định cấu hình trình phát và
truyền MediaItem
tương ứng, như mô tả ở trên.
Khi tạo MediaItem
, MediaItem.localConfiguration.streamKeys
phải được
được thiết lập để khớp với các thông báo trong DownloadRequest
để người chơi chỉ cố gắng
phát một nhóm bản nhạc đã được tải xuống. Sử dụng
Download.request.toMediaItem
và DownloadRequest.toMediaItem
để tạo
MediaItem
sẽ xử lý vấn đề này cho bạn.