Pobieram multimedia

ExoPlayer udostępnia funkcję pobierania multimediów do odtwarzania offline. Najwięcej zaleca się, aby pobieranie było możliwe nawet wtedy, gdy aplikacja znajduje się w tle. W tych przypadkach aplikacja powinna podklasa: DownloadService i wysyłania do usługi poleceń w celu dodania, usunięcia i kontrolowania pobranych plików. na diagramie poniżej widać główne klasy, które są z nim związane.

Zajęcia dotyczące pobierania multimediów. Kierunki strzałek wskazują przepływ danych.

  • DownloadService: zawija obiekt DownloadManager i przekazuje do niego polecenia. umożliwia działanie aplikacji DownloadManager nawet wtedy, gdy jest ona uruchomiona w tle.
  • DownloadManager: zarządza wieloma pobieraniem, wczytuje (i zapisuje) ich pliki od (i do) DownloadIndex, rozpoczynając i zatrzymując pobieranie na podstawie dotyczące wymagań dotyczących m.in. połączenia sieciowego. Aby pobrać treści, menedżer zazwyczaj odczytuje dane pobierane z HttpDataSource i wpisz go w Cache.
  • DownloadIndex: zachowuje stan pobierania.

Tworzenie usługi DownloadService

Aby utworzyć klasę DownloadService, należy ją podklasyować i zaimplementować metody abstrakcyjne:

  • getDownloadManager(): zwraca wartość DownloadManager, która ma zostać użyta.
  • getScheduler(): zwraca opcjonalny element Scheduler, który może ponownie uruchomić usługi, gdy są spełnione wymagania dotyczące oczekujących pobrań. ExoPlayer udostępnia te implementacje:
    • PlatformScheduler, które używa JobScheduler (minimum to 21). Zobacz zapoznaj się z wymaganiami dotyczącymi uprawnień aplikacji w dokumentacji javadocs PlatformScheduler.
    • WorkManagerScheduler, który korzysta z WorkManagera.
  • getForegroundNotification(): zwraca powiadomienie, które wyświetla się, gdy usługa jest uruchomiona na pierwszym planie. Za pomocą DownloadNotificationHelper.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>

Zobacz DemoDownloadService i AndroidManifest.xml w ExoPlayer z konkretnym przykładem.

Tworzenie Menedżera pobierania

Ten fragment kodu pokazuje, jak utworzyć instancję DownloadManager, które może zwrócić 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 w wersji demonstracyjnej.

Dodaję pobrany plik

Aby dodać pobrany plik, utwórz DownloadRequest i wyślij go na DownloadService W przypadku strumieni adaptacyjnych użyj DownloadHelper. utwórz DownloadRequest. Poniżej 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 contentUri może często służyć jako contentId, ale aplikacje są bezpłatne. do każdego schematu identyfikatora, który najlepiej sprawdzi się w danym przypadku. DownloadRequest.Builder ma też kilku opcjonalnych ustawień. Na przykład można użyć setKeySetId i setData, aby: ustawić DRM i dane niestandardowe, które aplikacja ma powiązać z pobranym plikiem; . Typ MIME treści można też określić w polu setMimeType, jako wskazówkę w przypadkach, w których nie można wywnioskować typu treści na podstawie contentUri.

Po utworzeniu prośbę można wysłać do DownloadService, aby dodać pobieranie:

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 aplikacji DownloadService, a klasa Parametr foreground określa, czy usługa zostanie uruchomiona w na pierwszym planie. Jeśli aplikacja jest już na pierwszym planie, parametr foreground powinien być ustawiony na false, bo DownloadService będzie umieścić się na pierwszym planie, jeśli stwierdzi, że ma do wykonania zadanie.

Usuwam pobrane

Aby usunąć pobrany plik, wyślij do DownloadService polecenie usunięcia. gdzie contentId wskazuje pobrany plik:

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 będzie kontynuowane tylko wtedy, gdy zostaną spełnione 4 warunki:

  • Pobieranie nie ma powodu zatrzymania.
  • Pobieranie nie jest wstrzymywane.
  • Spełniono wymagania dotyczące pobierania plików. Wymagania mogą obejmować dotyczące dozwolonych typów sieci oraz tego, czy urządzenie być nieaktywne lub podłączone do ładowarki.
  • Maksymalna liczba równoległych pobrań nie została przekroczona.

Wszystkie te warunki można kontrolować, 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 specjalną wartość, która oznacza, że pobieranie nie zostało zatrzymane). Aplikacje, które zawierają Różne przyczyny zatrzymania pobierania mogą mieć różne wartości, aby śledzić przyczyny zatrzymania pobierania. Ustawianie i usuwanie przyczyny zatrzymania dla wszystkich pobierania działa tak samo jak ustawianie i usuwanie przyczyny zatrzymania jednorazowe pobranie, z tym że parametr contentId powinien mieć wartość null.

Jeśli przyczyna zatrzymania pobierania jest inna niż zero, zostanie on umieszczony w sekcji Download.STATE_STOPPED stan. Przyczyny zatrzymania są zachowywane w DownloadIndex, a także są przechowywane w przypadku zakończenia procesu zgłaszania, a także później.

Wstrzymywanie i wznawianie pobierania wszystkich plików

Pobieranie można wstrzymywać i wznawiać w następujący 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);

Po wstrzymaniu pobierania ich stan zmieni się na Download.STATE_QUEUED. W przeciwieństwie do ustawionych przyczyn zatrzymania ta metoda nie zachowuje żadnego stanu zmian. Ma to wpływ tylko na stan środowiska wykonawczego DownloadManager.

Konfigurowanie wymagań dotyczących postępu pobierania

Requirements może służyć do określania ograniczeń, które muszą być spełnione w przypadku pobrane pliki, aby kontynuować. Wymagania można określić, wywołując DownloadManager.setRequirements() podczas tworzenia DownloadManager, na przykład przykład powyżej. Można je również dynamicznie zmieniać, wysyłając polecenie do 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);

Jeśli nie można kontynuować pobierania z powodu niespełniania wymagań, będzie mieć stan Download.STATE_QUEUED. Możesz przesłać zapytanie o niespełnione DownloadManager.getNotMetRequirements().

Ustawianie maksymalnej liczby równoległego pobierania

Maksymalna liczba równoległych pobrań można ustawić, wywołując DownloadManager.setMaxParallelDownloads() Zwykle ma to miejsce, gdy tworząc DownloadManager, jak widać w przykładzie powyżej.

Gdy nie można kontynuować pobierania ze względu na maksymalną liczbę równoległych pobrań są już w toku, będą w stanie Download.STATE_QUEUED.

Pytanie dotyczące pobierania

Zapytanie DownloadIndex elementu DownloadManager może być wysyłane w przypadku stanu wszystkich pobierania, w tym tych, które zakończyły się lub zakończyły się niepowodzeniem. DownloadIndex można uzyskać, wywołując DownloadManager.getDownloadIndex(). Kursor, powtórzenia we wszystkich pobranych plikach można następnie uzyskać przez wywołanie DownloadIndex.getDownloads() Stan pojedynczego pobrania można wysyłać zapytania, wywołując DownloadIndex.getDownload().

DownloadManager udostępnia również DownloadManager.getCurrentDownloads(), które zwraca tylko stan bieżących (tj. nieukończonych lub nieudanych) pobrań. Ten jest przydatna przy aktualizowaniu powiadomień i innych komponentów interfejsu, które wyświetlają postęp i stan pobierania.

Słucham pobranych treści

Możesz dodać detektor do aplikacji DownloadManager, aby otrzymywać informacje o bieżącej zmiany stanu pobierania:

Kotlin

downloadManager.addListener(
  object : DownloadManager.Listener { // Override methods of interest here.
  }
)

Java

downloadManager.addListener(
    new DownloadManager.Listener() {
      // Override methods of interest here.
    });

Zobacz temat DownloadManagerListener w zajęciach DownloadTracker w aplikacji w wersji demonstracyjnej z konkretnego przykładu.

Odtwarzam pobrane treści

Odtwarzanie pobranych treści przypomina odtwarzanie treści online, z tą różnicą, że dane są odczytywane z pobranego pliku Cache, a nie przez sieć.

Aby odtworzyć pobrane treści, utwórz CacheDataSource.Factory, używając tego samego elementu Cache instancję, która została użyta do pobrania 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();

Czy ta sama instancja odtwarzacza będzie także używana do odtwarzania niepobranych treści interfejs CacheDataSource.Factory powinien być skonfigurowany jako tylko do odczytu, aby uniknąć pobieranie tych treści również podczas odtwarzania.

Po skonfigurowaniu odtwarzacza CacheDataSource.Factory będzie on mają dostęp do pobranych treści w celu ich odtwarzania. Odtworzenie pobranego pliku jest wtedy Wystarczy przekazać odpowiednie MediaItem do odtwarzacza. MediaItem można uzyskać z Download przy użyciu Download.request.toMediaItem lub bezpośrednio z usługi DownloadRequest przy użyciu DownloadRequest.toMediaItem.

Konfiguracja MediaSource

Poprzedni przykład udostępnia pamięć podręczną pobierania do odtwarzania wszystkich plików MediaItem Możesz też udostępnić pamięć podręczną pobierania dla pojedyncze instancje MediaSource, które mogą być przekazywane 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óżnych jakości (np. ścieżki wideo SD, HD i 4K). Może być też wiele ścieżek tego samego typu zawierających różne treści (np. wiele ścieżek audio w różnych językach).

W przypadku odtwarzania strumieniowego można skorzystać z selektora ścieżki, aby wybrać odtwarzanych utworów. Podobnie DownloadHelper może służyć do: wybrać utwory do pobrania. Typowe użycie: DownloadHelper wykonaj te czynności:

  1. Utwórz DownloadHelper, korzystając z jednego z DownloadHelper.forMediaItem . Przygotuj pomocnik 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);
    
  2. Opcjonalnie sprawdź domyślnie wybrane ścieżki za pomocą narzędzia getMappedTrackInfo i getTrackSelections oraz wprowadź korekty za pomocą clearTrackSelections, replaceTrackSelections i addTrackSelection.
  3. Utwórz DownloadRequest dla wybranych utworów, dzwoniąc pod numer getDownloadRequest Prośba może zostać przekazana do DownloadService w celu dodaj pobrany plik w sposób opisany powyżej.
  4. Zwolnij pomocnik, używając polecenia release().

Odtwarzanie pobranych treści adaptacyjnych wymaga skonfigurowania odtwarzacza i przekazując odpowiednią wartość MediaItem zgodnie z opisem powyżej.

Podczas tworzenia obiektu MediaItem MediaItem.localConfiguration.streamKeys musi być: jest ustawione na dopasowanie do tych w DownloadRequest, tak by odtwarzacz próbował tylko odtworzyć podzbiór pobranych utworów. Zastosowanie Download.request.toMediaItem i DownloadRequest.toMediaItem, aby utworzyć MediaItem zajmie się tym za Ciebie.