ExoPlayer מספק פונקציונליות להורדת מדיה להפעלה במצב אופליין. במרבית
מומלץ שההורדות יימשכו גם כשהאפליקציה נמצאת
רקע. בתרחישים לדוגמה האלה, האפליקציה שלך צריכה לתת מחלקה משנית ל-DownloadService
שולחים לשירות פקודות להוספה, להסרה או לשליטה בהורדות.
בתרשים הבא מוצגים הסוגים העיקריים של הגורמים המעורבים.
DownloadService
: הפונקציה אורזתDownloadManager
ומעבירה אליו פקודות. השירות מאפשר ל-DownloadManager
להמשיך לפעול גם כשהאפליקציה נמצאת את הרקע.DownloadManager
: ניהול של הורדות מרובות, טעינה (ואחסון) שלהן משתנה מ-(ואל)DownloadIndex
, התחלה ועצירה של הורדות מבוססות בדרישות כמו קישוריות רשת וכן הלאה. כדי להוריד את לרוב, המנהל יקרא את הנתונים שמורידיםHttpDataSource
, ולכתוב אותה בCache
.DownloadIndex
: שמירת מצבי ההורדות.
יצירת שירות הורדה
כדי ליצור DownloadService
, צריך לתת לו מחלקה משנית ולהטמיע אותו
שיטות מופשטות:
getDownloadManager()
: הפונקציה מחזירה את הערךDownloadManager
שיש להשתמש בו.getScheduler()
: מחזירהScheduler
אופציונלי, שיכול להפעיל מחדש את השירות כאשר מתמלאים הדרישות הנדרשות להתקדמות של הורדות ממתינות. ExoPlayer מספק את ההטמעות הבאות:PlatformScheduler
, שמשתמש בJobScheduler (ה-API המינימלי הוא 21). צפייה את מסמכי Java Docs של PlatformScheduler בנושא הדרישות לגבי הרשאות לאפליקציות.WorkManagerScheduler
, שמשתמש ב-WorkManager.
getForegroundNotification()
: מחזיר התראה שמוצגת השירות פועל בחזית. אפשר להשתמשDownloadNotificationHelper.buildProgressNotification
כדי ליצור התראה בסגנון ברירת המחדל.
לבסוף, מגדירים את השירות בקובץ 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>
צפייה ב-DemoDownloadService
וב-AndroidManifest.xml
ב-ExoPlayer
של הדגמה מעשית.
יצירת מנהל הורדות
קטע הקוד הבא מדגים איך ליצור DownloadManager
,
שאפשר להחזיר על ידי getDownloadManager()
ב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);
דוגמה קונקרטית לכך אפשר לראות בכתובת DemoUtil
באפליקציית ההדגמה.
הוספת הורדה
כדי להוסיף הורדה, צריך ליצור DownloadRequest
ולשלוח אותו אל
DownloadService
. לשידורים מותאמים, אפשר להשתמש ב-DownloadHelper
כדי
ליצור DownloadRequest
. הבאים
דוגמה שממחישה איך ליצור בקשת הורדה:
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
בדוגמה הזו, contentId
הוא מזהה ייחודי של התוכן. במקרים פשוטים,
לעיתים קרובות ניתן להשתמש ב-contentUri
בתור contentId
, אבל האפליקציות זמינות לשימוש בחינם
איזו סכמת מזהים הכי מתאימה לתרחיש לדוגמה שלהם. גם ב-DownloadRequest.Builder
יש
כמה מגדירים אופציונליים. לדוגמה, אפשר להשתמש ב-setKeySetId
וב-setData
כדי
להגדיר DRM ונתונים מותאמים אישית שהאפליקציה רוצה לשייך להורדה,
בהתאמה. אפשר לציין גם את סוג ה-MIME של התוכן באמצעות setMimeType
,
כרמז למקרים שבהם לא ניתן להסיק את סוג התוכן מ-contentUri
.
לאחר יצירת הבקשה, אפשר לשלוח את הבקשה אל DownloadService
כדי להוסיף את
הורדה:
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
בדוגמה הזו, MyDownloadService
הוא המחלקה המשנית DownloadService
של האפליקציה,
הפרמטר foreground
קובע אם השירות יתחיל
בחזית. אם האפליקציה כבר פועלת בחזית, אז foreground
בדרך כלל צריך להיות מוגדר ל-false
כי DownloadService
להציב את עצמו בחזית אם הוא קובע שהוא צריך לפעול.
מסיר הורדות
אפשר להסיר הורדה על ידי שליחת פקודת הסרה אל DownloadService
,
כאשר contentId
מזהה את ההורדה שיש להסיר:
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
אפשר גם להסיר את כל הנתונים שהורדו באמצעות
DownloadService.sendRemoveAllDownloads
התחלה ועצירה של הורדות
ההורדה תתבצע רק אם יתקיימו ארבעה תנאים:
- אין סיבה לעצירה של ההורדה.
- ההורדות לא מושהות.
- עומדים בדרישות בנוגע להתקדמות של הורדות. ניתן לציין את הדרישות מגבלות על סוגי הרשתות המותרים, וקובעים אם המכשיר לא פעיל או מחובר למטען.
- אין חריגה מהמספר המקסימלי של הורדות מקבילות.
אפשר לשלוט בכל התנאים האלה על ידי שליחת פקודות
DownloadService
הגדרה וניקוי של הסיבות להפסקת ההורדה
אפשר להגדיר סיבה לעצירה של הורדה אחת או של כולן:
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
יכול להיות כל ערך שאינו אפס (Download.STOP_REASON_NONE = 0
הוא
ערך מיוחד שפירושו שההורדה לא תופסק). אפליקציות שיש להן
סיבות רבות להפסקת הורדות יכולות להשתמש בערכים שונים כדי לעקוב
שמסבירה למה כל הורדה הופסקה. הגדרה וביטול של סיבת העצירה לכולם
הורדות פועלות באותו אופן כמו הגדרה וניקוי של סיבת העצירה
הורדה אחת, אבל contentId
צריך להיות מוגדר ל-null
.
אם הסיבה של הורדה אינה אפס, היא תופיע
מצב Download.STATE_STOPPED
. סיבות העצירה נותרות
DownloadIndex
, וכך יישמרו אם תהליך הבקשה עוצר
הופעלו מחדש מאוחר יותר.
השהיה והמשך של כל ההורדות
ניתן להשהות ולהמשיך את כל ההורדות באופן הבא:
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);
כשההורדות מושהות, הן יהיו במצב Download.STATE_QUEUED
.
בניגוד להגדרת סיבות לעצירה, הגישה הזו לא נשארת באף מצב.
שינויים. היא משפיעה רק על מצב זמן הריצה של DownloadManager
.
הגדרת הדרישות להתקדמות של הורדות
אפשר להשתמש בפונקציה Requirements
כדי לציין מגבלות שצריכות להתקיים עבור
הורדות כדי להמשיך. אפשר לקבוע את הדרישות בטלפון
DownloadManager.setRequirements()
בזמן היצירה של DownloadManager
, כמו
בדוגמה שלמעלה. אפשר גם לשנות אותן באופן דינמי באמצעות שליחת פקודה
אל 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);
אם לא ניתן להמשיך בהורדה מפני שההורדה לא עומדת בדרישות,
יהיו במצב Download.STATE_QUEUED
. אפשר לשלוח שאילתה על חוסר עמידה
דרישות ב-DownloadManager.getNotMetRequirements()
.
הגדרת המספר המקסימלי של הורדות מקבילות
ניתן להגדיר את המספר המקסימלי של הורדות מקבילות באמצעות קריאה
DownloadManager.setMaxParallelDownloads()
בדרך כלל זה נעשה כאשר
בתהליך היצירה של DownloadManager
, כמו בדוגמה שלמעלה.
מתי ההורדה לא יכולה להמשיך בגלל המספר המקסימלי של הורדות מקבילות
כבר בתהליך. הוא יהיה במצב Download.STATE_QUEUED
.
שליחת שאילתות להורדות
אפשר להריץ שאילתה על הערך של DownloadIndex
של DownloadManager
לגבי המצב של כולם
כולל הורדות שהושלמו או נכשלו. DownloadIndex
אפשר לקבל באמצעות הפקודה DownloadManager.getDownloadIndex()
. סמן
ניתן לקבל באיטרציות על כל ההורדות באמצעות קריאה
DownloadIndex.getDownloads()
לחלופין, המצב של הורדה יחידה
ניתן לחפש שאילתה על ידי חיוג אל DownloadIndex.getDownload()
.
DownloadManager
מספק גם את DownloadManager.getCurrentDownloads()
,
מחזירה את המצב של ההורדות הנוכחיות (כלומר, לא הושלמו או נכשלו) בלבד. הזה
שימושית לעדכון התראות ורכיבים אחרים בממשק המשתמש
ההתקדמות והסטטוס של ההורדות הנוכחיות.
האזנה להורדות
יש לך אפשרות להוסיף האזנה ל-DownloadManager
כדי לקבל הודעה כשהגרסה הנוכחית
שינוי מצב הורדות:
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
אפשר לראות את DownloadManagerListener
בכיתה DownloadTracker
של אפליקציית ההדגמה
דוגמה קונקרטית.
הפעלה של תוכן שהורדת
הפעלת תוכן שהורד דומה להפעלת תוכן באינטרנט, מלבד
קריאה בנתונים מההורדה Cache
במקום דרך הרשת.
כדי להפעיל תוכן שהורדת, צריך ליצור CacheDataSource.Factory
באמצעות אותו
מופע אחד (Cache
) ששימש להורדה והחדר אותו אל
DefaultMediaSourceFactory
כשיוצרים את הנגן:
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();
אם אותו מופע נגן ישמש גם להפעלת תוכן שלא הורד
אז יש להגדיר את CacheDataSource.Factory
לקריאה בלבד כדי להימנע
להוריד את התוכן הזה גם במהלך ההפעלה.
אחרי הגדרת הנגן באמצעות CacheDataSource.Factory
,
לקבל גישה לתוכן שהורד להפעלה. לאחר מכן, הפעלת הורדה
צריך רק להעביר את MediaItem
התואם לנגן. MediaItem
אפשר לקבל מ-Download
באמצעות Download.request.toMediaItem
, או
ישירות מ-DownloadRequest
באמצעות DownloadRequest.toMediaItem
.
הגדרת MediaSource
הדוגמה הקודמת מאפשרת להפעיל את המטמון של ההורדות
MediaItem
שנ'. אפשר גם להפוך את מטמון ההורדות לזמין עבור
מופעים נפרדים של MediaSource
, שניתן להעביר ישירות לנגן:
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();
הורדה והפעלה של שידורים מותאמים
שידורים מותאמים (למשל DASH, SmoothStreaming ו-HLS) בדרך כלל מכילים טראקים של מדיה. לעיתים קרובות יש מספר טראקים שמכילים את אותו תוכן איכויות שונות (למשל: טראקים של וידאו באיכות SD, HD ו-4K). ייתכן שיש גם מספר טראקים מאותו סוג שמכילים תוכן שונה (למשל, טראקים של אודיו בשפות שונות).
להפעלות סטרימינג, ניתן להשתמש בבורר טראקים כדי לבחור
הטראקים האלה יופעלו. באופן דומה, אפשר להשתמש ב-DownloadHelper
להורדה,
לבחור אילו מהטראקים להוריד. שימוש אופייני ב-DownloadHelper
פועל לפי השלבים הבאים:
- בניית
DownloadHelper
באמצעות אחד מ-DownloadHelper.forMediaItem
שיטות. מכינים את ה-Helper וממתינים לקריאה החוזרת.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);
- אפשר גם לבדוק את הטראקים שנבחרו כברירת מחדל באמצעות
getMappedTrackInfo
וגםgetTrackSelections
, ולבצע התאמות באמצעותclearTrackSelections
,replaceTrackSelections
וגםaddTrackSelection
. - יצירת
DownloadRequest
לטראקים שנבחרו בטלפוןgetDownloadRequest
. ניתן להעביר את הבקשה אלDownloadService
אל מוסיפים את ההורדה, כמו שמתואר למעלה. - שחרור כלי העזר באמצעות
release()
.
כדי להפעיל תוכן מותאם שהורדתם, יש להגדיר את הנגן
העברת הערך המתאים של MediaItem
, כמו שמתואר למעלה.
כשיוצרים את MediaItem
, MediaItem.localConfiguration.streamKeys
חייב להיות
מוגדר להתאמה לאלה שבDownloadRequest
, כך שהשחקן ינסה רק
להפעיל את קבוצת המשנה של הטראקים שהורדתם. באמצעות
Download.request.toMediaItem
ו-DownloadRequest.toMediaItem
כדי ליצור את
MediaItem
יטפל בזה בשבילך.