ExoPlayer permet de télécharger des contenus multimédias pour les lire hors connexion. Dans la plupart des cas d'utilisation, il est souhaitable que les téléchargements se poursuivent même lorsque votre application est en arrière-plan. Pour ces cas d'utilisation, votre application doit créer une sous-classe DownloadService et envoyer des commandes au service pour ajouter, supprimer et contrôler les téléchargements. Le diagramme suivant montre les principales classes impliquées.
DownloadService: encapsule unDownloadManageret lui transmet les commandes. Le service permet àDownloadManagerde continuer à s'exécuter même lorsque l'application est en arrière-plan.DownloadManager: gère plusieurs téléchargements, charge (et stocke) leurs états à partir d'unDownloadIndex(et vers celui-ci), démarre et arrête les téléchargements en fonction des exigences telles que la connectivité réseau, etc. Pour télécharger le contenu, le gestionnaire lit généralement les données téléchargées à partir d'unHttpDataSourceet les écrit dans unCache.DownloadIndex: conserve l'état des téléchargements.
Créer un DownloadService
Pour créer un DownloadService, créez une sous-classe et implémentez ses méthodes abstraites :
getDownloadManager(): renvoie leDownloadManagerà utiliser.getScheduler(): renvoie unSchedulerfacultatif, qui peut redémarrer le service lorsque les exigences nécessaires à la progression des téléchargements en attente sont remplies. ExoPlayer fournit les implémentations suivantes :PlatformScheduler, qui utilise JobScheduler (l'API minimale est 21). Consultez la documentation Java de PlatformScheduler pour connaître les exigences d'autorisation de l'application.WorkManagerScheduler, qui utilise WorkManager.
getForegroundNotification(): renvoie une notification à afficher lorsque le service est exécuté au premier plan. Vous pouvez utiliserDownloadNotificationHelper.buildProgressNotificationpour créer une notification dans le style par défaut.
Enfin, définissez le service dans votre fichier 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>
Pour obtenir un exemple concret, consultez DemoDownloadService et AndroidManifest.xml dans l'application de démonstration ExoPlayer.
Création d'un gestionnaire de téléchargement
L'extrait de code suivant montre comment instancier un DownloadManager, qui peut être renvoyé par getDownloadManager() dans votre 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);
Pour obtenir un exemple concret, consultez DemoUtil dans l'application de démonstration.
Ajout d'un téléchargement
Pour ajouter un téléchargement, créez un DownloadRequest et envoyez-le à votre DownloadService. Pour les flux adaptatifs, utilisez DownloadHelper pour vous aider à créer un DownloadRequest. L'exemple suivant montre comment créer une demande de téléchargement :
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
Dans cet exemple, contentId est un identifiant unique pour le contenu. Dans les cas simples, le contentUri peut souvent être utilisé comme le contentId, cependant les applications sont libres d'utiliser le schéma d'identification qui convient le mieux à leur cas d'utilisation. DownloadRequest.Builder possède également quelques setters optionnels. Par exemple, setKeySetId et setData peuvent être utilisés pour définir respectivement les données DRM et les données personnalisées que l'application souhaite associer au téléchargement. Le type MIME du contenu peut également être spécifié à l'aide de setMimeType, comme indication dans les cas où le type de contenu ne peut pas être déduit de contentUri.
Une fois la requête créée, vous pouvez l'envoyer à DownloadService pour ajouter le téléchargement :
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
Dans cet exemple, MyDownloadService est la sous-classe DownloadService de l'application, et le paramètre foreground contrôle si le service sera démarré au premier plan. Si votre application est déjà au premier plan, alors le paramètre foreground doit normalement être défini sur false car le DownloadService se mettra au premier plan s'il détermine qu'il a du travail à faire.
Suppression des téléchargements…
Un téléchargement peut être supprimé en envoyant une commande de suppression à DownloadService, où contentId identifie le téléchargement à supprimer :
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
Vous pouvez également supprimer toutes les données téléchargées avec DownloadService.sendRemoveAllDownloads.
Démarrer et arrêter des téléchargements
Un téléchargement ne progresse que si quatre conditions sont remplies :
- Le téléchargement n'a pas de raison d'arrêt.
- Les téléchargements ne sont pas mis en pause.
- Les conditions requises pour que les téléchargements progressent sont remplies. Les exigences peuvent spécifier des contraintes sur les types de réseau autorisés, ainsi que sur l'état d'inactivité ou de connexion à un chargeur de l'appareil.
- Le nombre maximal de téléchargements parallèles n'est pas dépassé.
Vous pouvez contrôler toutes ces conditions en envoyant des commandes à votre DownloadService.
Définir et supprimer les motifs d'arrêt des téléchargements
Vous pouvez indiquer une raison pour l'arrêt d'un ou de tous les téléchargements :
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 peut être n'importe quelle valeur non nulle (Download.STOP_REASON_NONE = 0 est une valeur spéciale signifiant que le téléchargement n'est pas arrêté). Les applications qui ont plusieurs raisons d'arrêter les téléchargements peuvent utiliser différentes valeurs pour suivre la raison pour laquelle chaque téléchargement est arrêté. La définition et l'effacement de la raison de l'arrêt pour tous les téléchargements fonctionnent de la même manière que pour un seul téléchargement, sauf que contentId doit être défini sur null.
Lorsqu'un téléchargement a une raison d'arrêt non nulle, il se trouve dans l'état Download.STATE_STOPPED. Les raisons d'arrêt sont conservées dans DownloadIndex et sont donc conservées si le processus d'application est arrêté puis redémarré.
Suspendre et reprendre tous les téléchargements
Vous pouvez suspendre et reprendre tous les téléchargements comme suit :
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);
Lorsque les téléchargements sont suspendus, ils sont à l'état Download.STATE_QUEUED.
Contrairement à la définition des raisons d'arrêt, cette approche ne conserve aucune modification d'état. Cela n'affecte que l'état d'exécution de DownloadManager.
Définir les conditions requises pour que les téléchargements progressent
Requirements peut être utilisé pour spécifier les contraintes à respecter pour que les téléchargements puissent se poursuivre. Les exigences peuvent être définies en appelant DownloadManager.setRequirements() lors de la création de DownloadManager, comme dans l'exemple ci-dessus. Elles peuvent également être modifiées de manière dynamique en envoyant une commande à 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);
Lorsqu'un téléchargement ne peut pas se poursuivre, car les exigences ne sont pas respectées, il est à l'état Download.STATE_QUEUED. Vous pouvez interroger les exigences non respectées avec DownloadManager.getNotMetRequirements().
Définition du nombre maximal de téléchargements parallèles
Le nombre maximal de téléchargements parallèles peut être défini en appelant DownloadManager.setMaxParallelDownloads(). Cela se fait normalement lors de la création de DownloadManager, comme dans l'exemple ci-dessus.
Lorsqu'un téléchargement ne peut pas se poursuivre, car le nombre maximal de téléchargements parallèles est déjà en cours, il passe à l'état Download.STATE_QUEUED.
Interroger les téléchargements
Le DownloadIndex d'un DownloadManager peut être interrogé pour connaître l'état de tous les téléchargements, y compris ceux qui sont terminés ou qui ont échoué. Le DownloadIndex peut être obtenu en appelant DownloadManager.getDownloadIndex(). Un curseur qui parcourt tous les téléchargements peut alors être obtenu en appelant DownloadIndex.getDownloads(). Vous pouvez également interroger l'état d'un téléchargement unique en appelant DownloadIndex.getDownload().
DownloadManager fournit également DownloadManager.getCurrentDownloads(), qui renvoie l'état des téléchargements en cours (c'est-à-dire non terminés ni échoués) uniquement. Cette méthode est utile pour mettre à jour les notifications et autres composants d'interface utilisateur qui affichent la progression et l'état des téléchargements en cours.
Écouter des contenus téléchargés
Vous pouvez ajouter un écouteur à DownloadManager pour être informé lorsque l'état des téléchargements en cours change :
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
Pour obtenir un exemple concret, consultez DownloadManagerListener dans la classe DownloadTracker de l'application de démonstration.
Lire du contenu téléchargé
La lecture de contenu téléchargé est semblable à la lecture de contenu en ligne, sauf que les données sont lues à partir du téléchargement Cache au lieu d'être lues sur le réseau.
Pour lire le contenu téléchargé, créez un CacheDataSource.Factory à l'aide de la même instance Cache que celle utilisée pour le téléchargement, puis injectez-le dans DefaultMediaSourceFactory lors de la création du lecteur :
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();
Si la même instance de lecteur est également utilisée pour lire du contenu non téléchargé, CacheDataSource.Factory doit être configuré en lecture seule pour éviter de télécharger également ce contenu pendant la lecture.
Une fois le lecteur configuré avec CacheDataSource.Factory, il aura accès au contenu téléchargé pour la lecture. Pour lire un téléchargement, il suffit de transmettre le MediaItem correspondant au lecteur. Vous pouvez obtenir un MediaItem à partir d'un Download à l'aide de Download.request.toMediaItem ou directement à partir d'un DownloadRequest à l'aide de DownloadRequest.toMediaItem.
Configuration de MediaSource
L'exemple précédent rend le cache de téléchargement disponible pour la lecture de tous les MediaItem. Vous pouvez également rendre le cache de téléchargement disponible pour des instances MediaSource individuelles, qui peuvent être transmises directement au lecteur :
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();
Télécharger et lire des flux adaptatifs
Les flux adaptatifs (DASH, SmoothStreaming et HLS, par exemple) contiennent normalement plusieurs pistes multimédias. Il existe souvent plusieurs pistes contenant le même contenu, mais de différentes qualités (par exemple, des pistes vidéo SD, HD et 4K). Il peut également y avoir plusieurs pistes du même type contenant des contenus différents (par exemple, plusieurs pistes audio dans différentes langues).
Pour les lectures en streaming, un sélecteur de pistes peut être utilisé pour choisir les pistes à lire. De même, pour le téléchargement, un DownloadHelper peut être utilisé pour choisir les pistes à télécharger. L'utilisation typique d'un DownloadHelper
suit les étapes suivantes :
- Créez un
DownloadHelperà l'aide d'une instanceDownloadHelper.Factory. Préparez l'assistant et attendez le rappel.Kotlin
val downloadHelper = DownloadHelper.Factory() .setRenderersFactory(DefaultRenderersFactory(context)) .setDataSourceFactory(dataSourceFactory) .create(MediaItem.fromUri(contentUri)) downloadHelper.prepare(callback)
Java
DownloadHelper downloadHelper = new DownloadHelper.Factory() .setRenderersFactory(new DefaultRenderersFactory(context)) .setDataSourceFactory(dataSourceFactory) .create(MediaItem.fromUri(contentUri)); downloadHelper.prepare(callback);
- Vous pouvez également inspecter les pistes sélectionnées par défaut à l'aide de
getMappedTrackInfoetgetTrackSelections, et les ajuster à l'aide declearTrackSelections,replaceTrackSelectionsetaddTrackSelection. - Créez un
DownloadRequestpour les pistes sélectionnées en appelantgetDownloadRequest. La requête peut être transmise à votreDownloadServicepour ajouter le téléchargement, comme décrit ci-dessus. - Libérez l'outil d'aide à l'aide de
release().
Pour lire du contenu adaptatif téléchargé, vous devez configurer le lecteur et transmettre le MediaItem correspondant, comme décrit ci-dessus.
Lors de la création du MediaItem, MediaItem.localConfiguration.streamKeys doit être défini pour correspondre à ceux du DownloadRequest afin que le lecteur ne tente de lire que le sous-ensemble de pistes qui ont été téléchargées. L'utilisation de Download.request.toMediaItem et DownloadRequest.toMediaItem pour créer MediaItem s'en chargera pour vous.