Medien werden heruntergeladen

ExoPlayer bietet Funktionen zum Herunterladen von Medien für die Offlinewiedergabe. In den meisten Anwendungsfällen sollten die Downloads auch dann fortgesetzt werden, wenn sich Ihre App im Hintergrund. Für diese Anwendungsfälle sollte Ihre App die Klassen DownloadService und Befehle zum Hinzufügen, Entfernen und Steuern der Downloads an den Dienst senden. Die Das folgende Diagramm zeigt die beteiligten Hauptklassen.

Klassen zum Herunterladen von Medien. Die Pfeilrichtungen zeigen den Datenfluss an.

  • DownloadService: Fügt eine DownloadManager ein und leitet Befehle an diese weiter. Die ermöglicht die Ausführung von DownloadManager, auch wenn sich die App in im Hintergrund.
  • DownloadManager: Verwaltet mehrere Downloads und lädt (und speichert) ihre eigenen Status von (und an) einem DownloadIndex, wobei Downloads basierend auf wie die Netzwerkverbindung usw. So laden Sie die liest der Manager in der Regel die von einem HttpDataSource und schreiben Sie es in eine Cache.
  • DownloadIndex: Der Status der Downloads bleibt bestehen.

DownloadService erstellen

Um ein DownloadService zu erstellen, erstellen Sie abgeleitete Klassen davon und implementieren Sie abstrakte Methoden:

  • getDownloadManager(): Gibt die zu verwendende DownloadManager zurück.
  • getScheduler(): Gibt ein optionales Scheduler-Objekt zurück, das den wenn die Anforderungen für ausstehende Downloads erfüllt sind. ExoPlayer bietet folgende Implementierungen: <ph type="x-smartling-placeholder">
      </ph>
    • PlatformScheduler verwendet JobScheduler (Minimum API ist 21). Weitere Informationen finden Sie unter In der PlatformScheduler-Javadocs für die Anforderungen an App-Berechtigungen
    • WorkManagerScheduler verwendet WorkManager.
  • getForegroundNotification(): Gibt eine Benachrichtigung zurück, die angezeigt wird, wenn der der Dienst im Vordergrund ausgeführt wird. Sie können DownloadNotificationHelper.buildProgressNotification zum Erstellen Benachrichtigung im Standardstil.

Definieren Sie abschließend den Dienst in der Datei 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>

Siehe DemoDownloadService und AndroidManifest.xml im ExoPlayer Demo-App ein konkretes Beispiel.

DownloadManager erstellen

Das folgende Code-Snippet zeigt, wie eine DownloadManager instanziiert wird. Dieser kann von getDownloadManager() in deinem DownloadService zurückgegeben werden:

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);

Ein konkretes Beispiel finden Sie in der Demo-App unter DemoUtil.

Download hinzufügen

Wenn du einen Download hinzufügen möchtest, erstelle eine DownloadRequest und sende sie an dein DownloadService. Bei adaptiven Streams verwenden Sie DownloadHelper, um die DownloadRequest erstellen Die folgenden Beispiel für die Erstellung einer Downloadanfrage:

Kotlin

val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()

Java

DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();

In diesem Beispiel ist contentId eine eindeutige Kennung für den Inhalt. In einfachen Fällen contentUri kann oft als contentId verwendet werden, die Nutzung der Apps ist jedoch kostenlos ID-Schema, das sich am besten für ihren Anwendungsfall eignet. DownloadRequest.Builder hat auch einige optionale Setter. Beispielsweise können setKeySetId und setData für Folgendes verwendet werden: DRM und benutzerdefinierte Daten festlegen, die die App mit dem Download verknüpfen möchte, . Der MIME-Typ des Inhalts kann auch mit setMimeType angegeben werden, als Hinweis in Fällen, in denen der Inhaltstyp nicht von contentUri abgeleitet werden kann.

Nach der Erstellung kann die Anfrage an den DownloadService gesendet werden, um den Herunterladen:

Kotlin

DownloadService.sendAddDownload(
  context,
  MyDownloadService::class.java,
  downloadRequest,
  /* foreground= */ false
)

Java

DownloadService.sendAddDownload(
    context, MyDownloadService.class, downloadRequest, /* foreground= */ false);

In diesem Beispiel ist MyDownloadService die abgeleitete DownloadService-Klasse der Anwendung und der Der Parameter foreground steuert, ob der Dienst im im Vordergrund. Wenn deine App bereits im Vordergrund ausgeführt wird, ist der foreground normalerweise auf false gesetzt werden, weil DownloadService sich selbst in den Vordergrund rückt, wenn es feststellt, dass es Arbeit zu erledigen hat.

Downloads werden entfernt

Um einen Download zu entfernen, senden Sie einen Befehl zum Entfernen an DownloadService. Dabei gibt contentId den zu entfernenden Download an:

Kotlin

DownloadService.sendRemoveDownload(
  context,
  MyDownloadService::class.java,
  contentId,
  /* foreground= */ false
)

Java

DownloadService.sendRemoveDownload(
    context, MyDownloadService.class, contentId, /* foreground= */ false);

Sie können alle heruntergeladenen Daten auch mit DownloadService.sendRemoveAllDownloads

Downloads starten und beenden

Ein Download wird nur fortgesetzt, wenn vier Bedingungen erfüllt sind:

  • Der Download hat keinen Stoppgrund.
  • Downloads werden nicht pausiert.
  • Die Voraussetzungen für den Fortschritt von Downloads sind erfüllt. Anforderungen können angeben, welche Netzwerktypen zulässig sind und ob das Gerät inaktiv oder an ein Ladegerät angeschlossen.
  • Die maximale Anzahl paralleler Downloads wird nicht überschritten.

All diese Bedingungen können durch Senden von Befehlen an Ihren DownloadService

Gründe für das Stoppen des Downloads festlegen und löschen

Sie können einen Grund für das Beenden eines oder aller Downloads angeben:

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 kann ein beliebiger Wert ungleich null sein (Download.STOP_REASON_NONE = 0 ist ein spezieller Wert, d. h., der Download wird nicht angehalten). Apps mit Mehrere Gründe für das Beenden von Downloads können unterschiedliche Werte verwenden, um den Überblick zu behalten. warum die einzelnen Downloads gestoppt wurden. Stoppgrund für alle festlegen und löschen funktioniert genauso wie das Festlegen und Löschen des Stoppgrunds einzelner Download, außer dass contentId auf null gesetzt werden sollte.

Wenn ein Download einen Stoppgrund ungleich null hat, befindet er sich im Download.STATE_STOPPED. Stoppgründe bleiben im DownloadIndex und bleiben erhalten, wenn der Anwendungsprozess abgebrochen wird und neu gestartet.

Alle Downloads anhalten und fortsetzen

Alle Downloads können so pausiert und fortgesetzt werden:

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);

Wenn Downloads pausiert sind, haben sie den Status Download.STATE_QUEUED. Anders als beim Festlegen von Gründen für das Stoppen behält dieser Ansatz keinen Status bei. Änderungen. Sie wirkt sich nur auf den Laufzeitstatus von DownloadManager aus.

Voraussetzungen für den Fortschritt von Downloads festlegen

Mit Requirements können Einschränkungen angegeben werden, die für herunterladen, um fortzufahren. Die Anforderungen können durch Aufrufen von DownloadManager.setRequirements() beim Erstellen der DownloadManager, wie in Beispiel. Sie können auch dynamisch geändert werden, indem ein Befehl zu 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);

Wenn ein Download nicht fortgesetzt werden kann, weil die Anforderungen nicht erfüllt sind, hat den Status Download.STATE_QUEUED. Sie können die nicht erfüllten mit DownloadManager.getNotMetRequirements().

Maximale Anzahl paralleler Downloads festlegen

Die maximale Anzahl paralleler Downloads kann durch Aufrufen von DownloadManager.setMaxParallelDownloads() Dies erfolgt normalerweise, wenn beim Erstellen von DownloadManager, wie im Beispiel oben.

Wenn ein Download nicht fortgesetzt werden kann, weil die maximale Anzahl paralleler Downloads bereits ausgeführt werden, hat sie den Status Download.STATE_QUEUED.

Downloads abfragen

Der DownloadIndex einer DownloadManager kann für den Status aller Downloads, einschließlich abgeschlossener oder fehlgeschlagener Downloads. Das DownloadIndex erhalten Sie durch Aufrufen von DownloadManager.getDownloadIndex(). Ein Cursor, der Iterationen für alle Downloads können dann abgerufen werden, indem DownloadIndex.getDownloads() Alternativ kann der Status eines einzelnen Downloads kann durch Aufrufen von DownloadIndex.getDownload() abgefragt werden.

DownloadManager bietet auch DownloadManager.getCurrentDownloads(), das gibt nur den Status der aktuellen Downloads zurück (d.h. nicht abgeschlossen oder fehlgeschlagen). Dieses -Methode ist nützlich, um Benachrichtigungen und andere UI-Komponenten zu aktualisieren, Fortschritt und Status aktueller Downloads.

Downloads anhören

Sie können DownloadManager einen Listener hinzufügen, der informiert wird, wenn Änderungsstatus der Downloads:

Kotlin

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

Java

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

Unter DownloadManagerListener in der Klasse DownloadTracker der Demo-App finden Sie ein konkretes Beispiel.

Heruntergeladene Inhalte werden wiedergegeben

Das Abspielen heruntergeladener Inhalte funktioniert ähnlich wie die Wiedergabe von Onlineinhalten, mit folgenden Ausnahmen: Daten werden aus dem Download-Cache statt über das Netzwerk gelesen.

Um heruntergeladene Inhalte wiederzugeben, erstelle ein CacheDataSource.Factory mit demselben Cache-Instanz, die zum Herunterladen verwendet wurde, und in dieses Element einfügen. DefaultMediaSourceFactory beim Erstellen des Players:

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();

Wenn dieselbe Playerinstanz auch für die Wiedergabe nicht heruntergeladener Inhalte verwendet wird sollte die CacheDataSource.Factory als schreibgeschützt konfiguriert werden, um zu vermeiden, während der Wiedergabe auch diese Inhalte heruntergeladen werden.

Sobald der Player mit CacheDataSource.Factory konfiguriert wurde, wird er auf die heruntergeladenen Inhalte zur Wiedergabe zugreifen können. Das Abspielen eines Downloads indem du einfach die entsprechende MediaItem an den Spieler übergibst. Ein MediaItem können aus einem Download mit Download.request.toMediaItem abgerufen werden oder mit DownloadRequest.toMediaItem direkt von einem DownloadRequest abrufen.

MediaSource-Konfiguration

Mit dem vorherigen Beispiel wird der Download-Cache für die Wiedergabe aller MediaItem Sek. Sie können den Download-Cache auch für andere einzelne MediaSource-Instanzen, die direkt an den Spieler übergeben werden können:

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();

Adaptive Streams herunterladen und wiedergeben

Adaptive Streams (z.B. DASH, SmoothStreaming und HLS) enthalten normalerweise mehrere Medientracks. Oft gibt es mehrere Tracks, die denselben Inhalt mit unterschiedlichen Qualitäten (z. B. SD-, HD- und 4K-Videotracks) Möglicherweise gibt es auch Mehrere Tracks desselben Typs mit unterschiedlichen Inhalten (z.B. mehrere Audiotracks in verschiedenen Sprachen).

Bei Streaming-Wiedergaben kann mit einer Titelauswahl festgelegt werden, welche der Titel abgespielt werden. Ähnlich kann ein DownloadHelper zum Herunterladen verwendet werden, um auswählen, welche Titel heruntergeladen werden sollen. Typische Nutzung eines DownloadHelper führt dazu folgende Schritte aus:

  1. Erstelle einen DownloadHelper mit einer der DownloadHelper.forMediaItem . Bereiten Sie das Hilfsprogramm vor und warten Sie auf den Rückruf.

    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. Optional: Inspizieren Sie die standardmäßig ausgewählten Tracks mit getMappedTrackInfo. und getTrackSelections und nehmen Sie Anpassungen mit clearTrackSelections vor, replaceTrackSelections und addTrackSelection.
  3. Erstelle eine DownloadRequest für die ausgewählten Tracks, indem du folgenden Befehl aufrufst: getDownloadRequest. Die Anfrage kann an DownloadService übergeben werden an fügen Sie den Download wie oben beschrieben hinzu.
  4. Geben Sie das Hilfsprogramm mit release() frei.

Für die Wiedergabe heruntergeladener adaptiver Inhalte müssen der Player und Übergeben des entsprechenden MediaItem wie oben beschrieben.

Beim Erstellen von MediaItem muss MediaItem.localConfiguration.streamKeys mit denen im DownloadRequest übereinstimmen, sodass der Spieler nur versucht, die heruntergeladenen Titel wiedergeben. Mit Download.request.toMediaItem und DownloadRequest.toMediaItem zum Erstellen von MediaItem übernimmt das für Sie.