يوفّر ExoPlayer وظيفة تنزيل الوسائط لتشغيلها بلا اتصال بالإنترنت. في معظم
فمن المستحسن استمرار التنزيلات حتى عندما يكون التطبيق
الخلفية. في حالات الاستخدام هذه، يجب أن يشتمل تطبيقك على الفئة الفرعية DownloadService
و
إرسال أوامر إلى الخدمة لإضافة عمليات التنزيل وإزالتها والتحكم فيها. تشير رسالة الأشكال البيانية
الفئات الرئيسية المشاركة في المخطط البياني التالي.
DownloadService
: يلوفDownloadManager
ويعيد توجيه الأوامر إليه. تشير رسالة الأشكال البيانية إنّ الخدمة تتيح مواصلة تشغيلDownloadManager
حتى عندما يكون التطبيق مفعلاً. الخلفية.DownloadManager
: إدارة عمليات التنزيل المتعددة وتحميلها (وتخزينها) من (وإلى)DownloadIndex
، بدءًا من عمليات التنزيل وإيقافها استنادًا إلى على متطلبات مثل الاتصال بالشبكة، وما إلى ذلك. لتنزيل سيقرأ المدير البيانات التي يتم تنزيلها منHttpDataSource
، وكتابته فيCache
.DownloadIndex
: تثبيت حالات عمليات التنزيل
إنشاء DownloadService
لإنشاء DownloadService
، ضع فئة فرعية ونفِّذها
الطرق التجريدية:
getDownloadManager()
: تعرض القيمةDownloadManager
المطلوب استخدامها.getScheduler()
: تعرض قيمةScheduler
اختيارية يمكن إعادة تشغيلها. الخدمة عند استيفاء المتطلبات اللازمة لعمليات التنزيل المعلّقة. يوفّر ExoPlayer آليات التنفيذ التالية:PlatformScheduler
، التي تستخدم Jobscheduler (الحد الأدنى لواجهة برمجة التطبيقات هو 21). عرض مستندات جافا 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
يوضح مقتطف الرمز التالي كيفية إنشاء مثيل 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
للأغراض التالية:
تعيين إدارة الحقوق الرقمية والبيانات المخصصة التي يرغب التطبيق في ربطها بالتنزيل،
على التوالي. ويمكن أيضًا تحديد نوع 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) عادةً على عدة خيارات مقاطع الوسائط. هناك غالبًا مقاطع صوتية متعددة تتضمن المحتوى نفسه في جودة مختلفة (مثل مقاطع فيديو بدقة عادية ودقة عالية وبدقة 4K) قد تكون هناك أيضًا مقاطع صوتية متعددة من النوع نفسه تتضمن محتوى مختلفًا (على سبيل المثال، مقاطع صوتية متعددة المقاطع الصوتية بلغات مختلفة).
بالنسبة إلى عمليات تشغيل البث، يمكن استخدام أداة اختيار المقاطع الصوتية لاختيار
التي يتم تشغيلها. وبالمثل، يمكن استخدام DownloadHelper
للتنزيل من أجل
لاختيار المقاطع الصوتية التي يتم تنزيلها. الاستخدام المعتاد لـ DownloadHelper
اتبع هذه الخطوات:
- إنشاء
DownloadHelper
باستخدام إحدىDownloadHelper.forMediaItem
الطرق. جهِّز تطبيق المساعد وانتظِر حتى يتم معاودة الاتصال.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
" هذه المسألة نيابةً عنك.