ExoPlayer umożliwia pobieranie multimediów do odtwarzania offline. W większości przypadków zalecane jest
kontynuowanie pobierania aplikacji nawet w tle. W takich przypadkach aplikacja powinna podklasyfikować DownloadService
i wysyłać do usługi polecenia pozwalające na dodawanie i usuwanie plików oraz zarządzanie pobranymi plikami. Poniższy diagram przedstawia główne klasy, które są uwzględniane.
DownloadService
: zawija obiektDownloadManager
i przekazuje do niego polecenia. Usługa umożliwia działanieDownloadManager
nawet wtedy, gdy aplikacja działa w tle.DownloadManager
: zarządza wielokrotnym pobieraniem, wczytuje (i zapisuje) ich stany z (i do)DownloadIndex
oraz uruchamia i wstrzymuje pobieranie na podstawie wymagań takich jak połączenie sieciowe itp. Aby pobrać treść, menedżer zwykle odczytuje dane pobierane z urządzeniaHttpDataSource
i zapisuje je w plikuCache
.DownloadIndex
: utrzymuje stan pobierania.
Tworzenie usługi DownloadService
Aby utworzyć DownloadService
, podklasuj go i zaimplementuj jego metody abstrakcyjne:
getDownloadManager()
: zwraca wartośćDownloadManager
do użycia.getScheduler()
: zwraca opcjonalny elementScheduler
, który może ponownie uruchomić usługę, gdy zostaną spełnione wymagania dotyczące postępu oczekujących pobierania. ExoPlayer udostępnia takie implementacje:PlatformScheduler
, który używa JobScheduler (minimum API to 21). Wymagania dotyczące uprawnień aplikacji znajdziesz w dokumentacji Java PlatformScheduler.WorkManagerScheduler
, która używa WorkManagera.
getForegroundNotification()
: zwraca powiadomienie, które jest wyświetlane, gdy usługa działa na pierwszym planie. Możesz użyćDownloadNotificationHelper.buildProgressNotification
, aby utworzyć powiadomienie w stylu domyślnym.
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>
Konkretny przykład znajdziesz w sekcji DemoDownloadService
i AndroidManifest.xml
w prezentacji aplikacji ExoPlayer.
Tworzenie menedżera pobierania
Ten fragment kodu pokazuje, jak utworzyć instancję DownloadManager
, która może zostać zwrócona przez getDownloadManager()
w 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 demonstracyjnej.
Dodawanie pobranego pliku
Aby dodać pobrany plik, utwórz dokument DownloadRequest
i wyślij go na urządzenie DownloadService
. W przypadku transmisji adaptacyjnych użyj parametru DownloadHelper
, aby utworzyć DownloadRequest
. Poniższy przykład pokazuje, jak utworzyć żądanie pobrania:
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
W tym przykładzie contentId
jest unikalnym identyfikatorem treści. W prostych przypadkach obiekt contentUri
może być często używany jako contentId
, ale aplikacje mogą korzystać z dowolnego schematu identyfikatora, który najlepiej pasuje do ich zastosowania. DownloadRequest.Builder
ma też opcjonalne ustawienia ustawiające. Na przykład zasady setKeySetId
i setData
mogą posłużyć do skonfigurowania DRM i danych niestandardowych, które aplikacja chce powiązać odpowiednio z pobieranym plikiem. Typ MIME treści można też określić za pomocą atrybutu setMimeType
, aby wskazać sytuacje, w których nie można wywnioskować typu treści z contentUri
.
Po utworzeniu można ją wysłać do DownloadService
, aby dodać dane do pobrania:
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 zostanie uruchomiona na pierwszym planie. Jeśli aplikacja jest już na pierwszym planie, parametr foreground
powinien zwykle mieć wartość false
, ponieważ DownloadService
pojawia się na pierwszym planie, jeśli uzna, że ma wykonać jakieś zadania.
Usuwam pobrane
Pobrany plik można usunąć, wysyłając do interfejsu DownloadService
polecenie usuwania, gdzie contentId
wskazuje dane do usunięcia:
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
Wszystkie pobrane dane możesz też usunąć w aplikacji 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 wstrzymywane.
- Wymagania dotyczące postępu pobierania są spełnione. Wymagania mogą określać ograniczenia dotyczące dozwolonych typów sieci oraz to, czy urządzenie powinno być bezczynne lub podłączone do ładowarki.
- Maksymalna liczba równoległych pobrań nie została przekroczona.
Wszystkimi tymi warunkami możesz zarządzać, wysyłając polecenia do: DownloadService
.
Ustawianie i usuwanie przyczyn zatrzymania pobierania
Możesz ustawić przyczynę 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 mieć dowolną wartość inną niż 0 (Download.STOP_REASON_NONE = 0
to wartość specjalna, która oznacza, że pobieranie nie jest zatrzymane). Aplikacje, które mają wiele powodów do zatrzymania pobierania, mogą używać różnych wartości do śledzenia przyczyn zatrzymania pobierania. Ustawianie i usuwanie przyczyny zatrzymania dla wszystkich pobierania działa tak samo jak ustawianie i usuwanie przyczyny zatrzymania w przypadku pojedynczego pobierania, z tym że contentId
ma wartość null
.
Jeśli przyczyna zatrzymania jest inna niż 0, pobieranie to jest w stanie Download.STATE_STOPPED
. Przyczyny zatrzymania są zachowane w DownloadIndex
, więc są przechowywane w przypadku zakończenia procesu aplikacji i późniejszego ponownego jego uruchomienia.
Wstrzymywanie i wznawianie pobierania wszystkich plików
Pobieranie wszystkich plików możesz 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 mają stan Download.STATE_QUEUED
.
W odróżnieniu od ustawiania powodów zatrzymania to podejście nie powoduje trwałych zmian stanu. Ma to wpływ tylko na stan środowiska wykonawczego DownloadManager
.
Ustawianie wymagań dotyczących postępu pobierania
Requirements
pozwala określić ograniczenia, które muszą zostać spełnione, aby można było kontynuować pobieranie. Wymagania można ustawić, wywołując DownloadManager.setRequirements()
podczas tworzenia obiektu DownloadManager
, jak w przykładzie powyżej. Można je też zmieniać dynamicznie, wysyłając do interfejsu 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);
Jeśli nie można kontynuować pobierania z powodu niespełnienia wymagań, plik będzie miał stan Download.STATE_QUEUED
. Zapytanie o niespełnione wymagania możesz wysłać za pomocą funkcji DownloadManager.getNotMetRequirements()
.
Ustawianie maksymalnej liczby równoległych pobrań
Maksymalną liczbę równoległych pobrań można ustawić za pomocą wywołania DownloadManager.setMaxParallelDownloads()
. Zwykle wykonuje się to podczas tworzenia DownloadManager
, jak w przykładzie powyżej.
Jeśli nie można kontynuować pobierania, ponieważ trwa już maksymalna liczba równoległych pobrań, stan to Download.STATE_QUEUED
.
Zapytania dotyczące pobranych plików
Do zapytania DownloadIndex
obiektu DownloadManager
można wysyłać zapytania o stan wszystkich operacji pobierania, w tym tych zakończonych lub niezaliczonych. Obiekt DownloadIndex
można uzyskać, wywołując metodę DownloadManager.getDownloadIndex()
. Kursor wykonujący iteracje dla wszystkich pobranych plików można następnie uzyskać, wywołując metodę DownloadIndex.getDownloads()
. Zapytanie o stan pojedynczego pobrania można też uzyskać, wywołując DownloadIndex.getDownload()
.
DownloadManager
udostępnia też parametr DownloadManager.getCurrentDownloads()
, który zwraca tylko stan bieżącego pobierania (tj. nie ukończono lub nie udało się pobrać). Ta metoda przydaje się do aktualizowania powiadomień i innych komponentów interfejsu, które wyświetlają informacje o postępie i stanie pobierania.
Odsłuchiwanie pobranych plików
Możesz dodać detektor do aplikacji DownloadManager
, aby otrzymywać informacje o zmianie stanu pobranych 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 znajdziesz w zasobie DownloadManagerListener
w klasie DownloadTracker
aplikacji demonstracyjnej.
Odtwarzanie pobranych treści
Odtwarzanie pobranych treści przypomina odtwarzanie treści online, z tą różnicą, że dane są odczytywane z pobieranego elementu Cache
, a nie przez sieć.
Aby odtworzyć pobrane treści, utwórz CacheDataSource.Factory
przy użyciu tej samej instancji Cache
, która była używana do pobierania, i wstrzyknij ją do 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 też używana do odtwarzania niepobranych treści, element CacheDataSource.Factory
powinien być skonfigurowany jako przeznaczony tylko do odczytu, aby uniknąć pobierania również tych treści podczas odtwarzania.
Gdy w odtwarzaczu skonfigurujesz CacheDataSource.Factory
, będzie on miał dostęp do pobranych treści i będzie mógł je odtwarzać. Odtworzenie pobranego pliku
sprowadza się do przekazania odpowiedniego elementu MediaItem
do odtwarzacza. MediaItem
można uzyskać z Download
przy użyciu Download.request.toMediaItem
lub bezpośrednio z DownloadRequest
przy użyciu DownloadRequest.toMediaItem
.
Konfiguracja MediaSource
Poprzedni przykład udostępnia pamięć podręczną pobierania do odtwarzania wszystkich elementów MediaItem
. Możesz też udostępnić pamięć podręczną pobierania dla poszczególnych instancji MediaSource
, które będą przesyłane 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 zawierających te same treści w różnej jakości (np. ścieżki wideo w rozdzielczości SD, HD i 4K). Może się też zdarzyć, że wiele ścieżek tego samego typu będzie zawierać różne treści (np. wiele ścieżek audio w różnych językach).
W przypadku odtwarzania strumieniowego za pomocą selektora ścieżek można wybrać utwór do odtworzenia. Podobnie w przypadku pobierania, za pomocą DownloadHelper
można wybrać utwory do pobrania. Typowe użycie DownloadHelper
występuje w ten sposób:
- Utwórz
DownloadHelper
, korzystając z jednej z metodDownloadHelper.forMediaItem
. Przygotuj pomocnika i zaczekaj na oddzwonienie.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 sprawdź domyślnie wybrane ścieżki za pomocą
getMappedTrackInfo
igetTrackSelections
oraz wprowadź zmiany za pomocąclearTrackSelections
,replaceTrackSelections
iaddTrackSelection
. - Aby utworzyć
DownloadRequest
dla wybranych ścieżek, wywołajgetDownloadRequest
. Prośba może zostać przekazana doDownloadService
w celu dodania pliku do pobrania w sposób opisany powyżej. - Zwolnij pomocnika za pomocą
release()
.
Odtwarzanie pobranych treści adaptacyjnych wymaga skonfigurowania odtwarzacza i przekazywania odpowiedniego parametru MediaItem
, jak opisano powyżej.
Podczas tworzenia pliku MediaItem
parametr MediaItem.localConfiguration.streamKeys
musi być ustawiony tak, aby pasował do tych w DownloadRequest
, aby odtwarzacz próbował odtwarzać tylko podzbiór pobranych utworów. Użycie Download.request.toMediaItem
i DownloadRequest.toMediaItem
do utworzenia MediaItem
pomoże Ci to zrobić.