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.
DownloadService: Fügt eineDownloadManagerein und leitet Befehle an diese weiter. Die ermöglicht die Ausführung vonDownloadManager, auch wenn sich die App in im Hintergrund.DownloadManager: Verwaltet mehrere Downloads und lädt (und speichert) ihre eigenen Status von (und an) einemDownloadIndex, wobei Downloads basierend auf wie die Netzwerkverbindung usw. So laden Sie die liest der Manager in der Regel die von einemHttpDataSourceund schreiben Sie es in eineCache.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 verwendendeDownloadManagerzurück.getScheduler(): Gibt ein optionalesScheduler-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>
PlatformSchedulerverwendet JobScheduler (Minimum API ist 21). Weitere Informationen finden Sie unter In der PlatformScheduler-Javadocs für die Anforderungen an App-BerechtigungenWorkManagerSchedulerverwendet WorkManager.
getForegroundNotification(): Gibt eine Benachrichtigung zurück, die angezeigt wird, wenn der der Dienst im Vordergrund ausgeführt wird. Sie könnenDownloadNotificationHelper.buildProgressNotificationzum 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:
- Erstelle einen
DownloadHelpermit einer derDownloadHelper.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);
- Optional: Inspizieren Sie die standardmäßig ausgewählten Tracks mit
getMappedTrackInfo. undgetTrackSelectionsund nehmen Sie Anpassungen mitclearTrackSelectionsvor,replaceTrackSelectionsundaddTrackSelection. - Erstelle eine
DownloadRequestfür die ausgewählten Tracks, indem du folgenden Befehl aufrufst:getDownloadRequest. Die Anfrage kann anDownloadServiceübergeben werden an fügen Sie den Download wie oben beschrieben hinzu. - 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.