ExoPlayer udostępnia funkcję pobierania multimediów na potrzeby odtwarzania offline. W większości przypadków dobrze jest, aby pobieranie było kontynuowane nawet wtedy, gdy aplikacja działa w tle. W takich przypadkach aplikacja powinna być podklasą DownloadService
i wysyłać do usługi polecenia dodawania, usuwania i sterowania pobieraniem. Na tym diagramie widać główne klasy, które są zaangażowane w proces.
DownloadService
: zawija obiektDownloadManager
i przekazuje do niego polecenia. Dzięki tej usłudze usługaDownloadManager
może działać nawet wtedy, gdy aplikacja działa w tle.DownloadManager
: zarządza wieloma pobieraniami, wczytuje (i przechowuje) ich stany z (i do)DownloadIndex
, uruchamia i zatrzymuje pobieranie na podstawie wymagań takich jak łączność z internetem itp. Aby pobrać zawartość, menedżer zwykle odczytuje dane pobierane zHttpDataSource
i zapisze je wCache
.DownloadIndex
: zachowuje stany pobierania.
Tworzenie usługi DownloadService
Aby utworzyć klasę DownloadService
, utwórz jej podklasę i zaimplementuj jej metody abstrakcyjne:
getDownloadManager()
: zwracaDownloadManager
do użycia.getScheduler()
: zwraca opcjonalny elementScheduler
, który może ponownie uruchomić usługę, gdy zostaną spełnione wymagania dotyczące oczekujących pobrań. ExoPlayer udostępnia te implementacje:PlatformScheduler
, który używa JobScheduler (minimalna wersja interfejsu API to 21). Wymagania dotyczące uprawnień aplikacji znajdziesz w dokumentacji javadocs PlatformScheduler.WorkManagerScheduler
, który korzysta z WorkManagera.
getForegroundNotification()
: zwraca powiadomienie wyświetlane, gdy usługa działa na pierwszym planie. Możesz użyć elementuDownloadNotificationHelper.buildProgressNotification
, aby utworzyć powiadomienie w domyślnym stylu.
Na koniec zdefiniuj usługę w pliku 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>
Przykładem jest element DemoDownloadService
i AndroidManifest.xml
w aplikacji demonstracyjnej ExoPlayer.
Tworzenie obiektu DownloadManager
Ten fragment kodu pokazuje, jak utworzyć instancję obiektu DownloadManager
, który może być zwracany przez funkcję getDownloadManager()
w Twojej funkcji DownloadService
:
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);
Konkretny przykład znajdziesz w sekcji DemoUtil
w aplikacji w wersji demonstracyjnej.
Dodawanie pliku do pobrania
Aby dodać plik do pobrania, utwórz DownloadRequest
i wyślij go do DownloadService
. W przypadku strumieni adaptacyjnych użyj DownloadHelper
, aby utworzyć DownloadRequest
. Ten przykład pokazuje, jak utworzyć żądanie pobierania:
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
W tym przykładzie contentId
to unikalny identyfikator treści. W prostych przypadkach contentUri
może być używane jako contentId
, ale aplikacje mogą stosować dowolny schemat identyfikatorów, który najlepiej pasuje do danego przypadku użycia. Obiekt DownloadRequest.Builder
ma też niektóre opcjonalne settery. Na przykład za pomocą setKeySetId
i setData
można odpowiednio ustawić DRM oraz dane niestandardowe, które aplikacja chce powiązać z pobieraniem. Typ MIME treści można też określić za pomocą atrybutu setMimeType
, który służy jako podpowiedź w przypadkach, gdy nie można określić typu treści na podstawie atrybutu contentUri
.
Utworzoną prośbę można wysłać do DownloadService
, aby dodać do niej pobrany plik:
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
W tym przykładzie MyDownloadService
to podklasa DownloadService
aplikacji, a parametr foreground
określa, czy usługa ma być uruchamiana na pierwszym planie. Jeśli aplikacja jest już na pierwszym planie, parametr foreground
powinien być ustawiony na false
, ponieważ DownloadService
przejdzie na pierwszy plan, jeśli stwierdzi, że ma coś do zrobienia.
Usuwam pobrane
Pobranie można usunąć, wysyłając do DownloadService
polecenie usunięcia, gdzie contentId
wskazuje plik do usunięcia:
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
Możesz też usunąć wszystkie pobrane dane za pomocą DownloadService.sendRemoveAllDownloads
.
Rozpoczynanie i zatrzymywanie pobierania
Pobieranie rozpocznie się tylko wtedy, gdy zostaną spełnione 4 warunki:
- Pobieranie nie ma powodu zatrzymania.
- Pobieranie nie jest wstrzymane.
- Spełniono wymagania dotyczące pobierania plików. Wymagania mogą określać ograniczenia dotyczące dozwolonych typów sieci, a także określać, czy urządzenie ma być nieaktywne czy podłączone do ładowarki.
- Nie jest przekraczana maksymalna liczba równoległych pobrań.
Wszystkie te warunki można kontrolować, wysyłając polecenia do urządzenia DownloadService
.
Ustawianie i usuwanie przyczyn zatrzymania pobierania
Możesz określić powód zatrzymania pobierania jednego lub wszystkich plików:
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
może być dowolną wartością niezerową (Download.STOP_REASON_NONE = 0
to wartość specjalna oznaczająca, że pobieranie nie zostało zatrzymane). Aplikacje, które mają wiele powodów zatrzymania pobierania, mogą używać różnych wartości do śledzenia, dlaczego pobieranie zostało zatrzymane. Ustawianie i czyszczenie powodu zatrzymania wszystkich plików do pobrania działa tak samo jak ustawianie i czyszczenie powodu zatrzymania pojedynczego pliku do pobrania, z tym że wartość contentId
powinna być ustawiona na null
.
Gdy pobieranie ma niezerową wartość powodu zatrzymania, jest w stanie Download.STATE_STOPPED
. Powody zatrzymania są przechowywane w DownloadIndex
, dzięki czemu są zachowywane, jeśli proces aplikacji zostanie zatrzymany i później ponownie uruchomiony.
Wstrzymywanie i wznawianie wszystkich pobieranych plików
Wszystkie pliki pobierane można wstrzymywać i wznawiać w ten sposób:
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);
Wstrzymane pobieranie będzie miało stan Download.STATE_QUEUED
.
W przeciwieństwie do ustawienia przyczyn zatrzymania to podejście nie zachowuje żadnych zmian stanu. Ma to wpływ tylko na stan DownloadManager
w czasie wykonywania.
Ustawianie wymagań dotyczących pobierania
Requirements
można użyć do określenia ograniczeń, które muszą zostać spełnione, aby można było pobrać plik. Wymagania można ustawić, wywołując DownloadManager.setRequirements()
podczas tworzenia DownloadManager
, jak w powyższym przykładzie. Można je też zmieniać dynamicznie, wysyłając do DownloadService
polecenie:
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);
Gdy pobieranie nie może zostać ukończone, ponieważ nie są spełnione wymagania, będzie mieć stan Download.STATE_QUEUED
. Za pomocą zapytania DownloadManager.getNotMetRequirements()
możesz sprawdzić wymagania, które nie zostały spełnione.
Ustawianie maksymalnej liczby równoległych pobrań
Maksymalną liczbę równoległych pobrań można ustawić, wywołując funkcję DownloadManager.setMaxParallelDownloads()
. Zwykle trzeba to zrobić podczas tworzenia obiektu DownloadManager
, jak w przykładzie powyżej.
Gdy pobieranie nie może się rozpocząć, ponieważ jest już w toku maksymalna liczba równoczesnych pobrań, jest w stanie Download.STATE_QUEUED
.
Przesyłanie zapytań dotyczących pobrań
Zapytanie DownloadIndex
elementu DownloadManager
można wysłać w przypadku stanu wszystkich pobrań, w tym tych, które zostały ukończone lub zakończyły się niepowodzeniem. DownloadIndex
można uzyskać, wywołując funkcję DownloadManager.getDownloadIndex()
. Kursor, który wyświetli wszystkie pobrane pliki, można uzyskać, wywołując funkcję DownloadIndex.getDownloads()
. Stan pojedynczego pobierania można też sprawdzić, wywołując funkcję DownloadIndex.getDownload()
.
DownloadManager
zawiera też DownloadManager.getCurrentDownloads()
, który zwraca stan tylko bieżących (czyli nieukończonych lub nieudanych) pobrań. Ta metoda jest przydatna do aktualizowania powiadomień i innych elementów interfejsu, które wyświetlają postęp i stan bieżących pobierania.
Słuchanie pobranych plików
Możesz dodać do DownloadManager
słuchacza, aby otrzymywać powiadomienia o zmianie stanu bieżących pobieranych plików:
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
Konkretny przykład zawiera DownloadManagerListener
w klasie DownloadTracker
aplikacji w wersji demonstracyjnej.
Odtwarzam pobrane treści
Odtwarzanie pobranych treści jest podobne do odtwarzania treści online, z tym że dane są odczytywane z pobranego pliku Cache
, a nie z sieci.
Aby odtworzyć pobrane treści, utwórz plik CacheDataSource.Factory
przy użyciu tego samego wystąpienia Cache
, które było używane do pobierania, i wstrzyknij go do obszaru DefaultMediaSourceFactory
podczas tworzenia odtwarzacza:
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();
Jeśli ta sama instancja odtwarzacza będzie używana do odtwarzania niepobranych treści, CacheDataSource.Factory
należy skonfigurować jako tylko do odczytu, aby uniknąć pobierania tych treści podczas odtwarzania.
Po skonfigurowaniu odtwarzacza za pomocą CacheDataSource.Factory
uzyska on dostęp do pobranych treści i będzie można je odtworzyć. Odtworzenie pobranego pliku jest tak proste, jak przekazanie odpowiedniego MediaItem
do odtwarzacza. Wartość MediaItem
można uzyskać z poziomu Download
za pomocą funkcji Download.request.toMediaItem
lub bezpośrednio z poziomu DownloadRequest
za pomocą funkcji DownloadRequest.toMediaItem
.
Konfiguracja MediaSource
Poprzedni przykład udostępnia pamięć podręczną pobierania na potrzeby odtwarzania wszystkich plików MediaItem
. Możesz też udostępnić pamięć podręczną pobierania dla poszczególnych instancji MediaSource
, które można przekazać bezpośrednio do odtwarzacza:
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();
Pobieranie i odtwarzanie strumieni adaptacyjnych
Strumienie adaptacyjne (np. DASH, SmoothStreaming i HLS) zwykle zawierają wiele ścieżek multimedialnych. Często istnieje wiele ścieżek z tymi samymi treściami w różnej jakości (np. ścieżki wideo SD, HD i 4K). Mogą też występować ścieżki tego samego typu zawierające różne treści (np. ścieżki audio w różnych językach).
W przypadku odtwarzania strumieniowego można użyć selektora utworów, aby wybrać, które utwory mają być odtwarzane. Podobnie w przypadku pobierania, DownloadHelper
może posłużyć do określenia, które utwory mają zostać pobrane. Typowe zastosowanie DownloadHelper
:
- Utwórz
DownloadHelper
, korzystając z jednej z metodDownloadHelper.forMediaItem
. Przygotuj pomocnika i zaczekaj na połączenie zwrotne.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);
- Opcjonalnie możesz sprawdzić domyślnie wybrane ścieżki za pomocą opcji
getMappedTrackInfo
igetTrackSelections
oraz wprowadzić korekty za pomocą opcjiclearTrackSelections
,replaceTrackSelections
iaddTrackSelection
. - Utwórz
DownloadRequest
dla wybranych utworów, dzwoniąc pod numergetDownloadRequest
. Prośbę można przekazać doDownloadService
, aby dodać możliwość pobrania, jak opisano powyżej. - Zwalnij pomocnika, używając
release()
.
Odtwarzanie pobranych treści dostosowanych wymaga skonfigurowania odtwarzacza i przekazania odpowiedniego MediaItem
, jak opisano powyżej.
Podczas tworzenia MediaItem
musisz ustawić MediaItem.localConfiguration.streamKeys
tak, aby odpowiadał wartościom w DownloadRequest
, aby odtwarzacz próbował odtworzyć tylko podzbiór pobranych utworów. Użycie Download.request.toMediaItem
i DownloadRequest.toMediaItem
do utworzenia MediaItem
załatwi Ci to zadanie.