ExoPlayer قابلیت دانلود رسانه برای پخش آفلاین را فراهم می کند. در بیشتر موارد استفاده، مطلوب است که دانلودها حتی زمانی که برنامه شما در پسزمینه است ادامه یابد. برای این موارد استفاده، برنامه شما باید DownloadService
را زیر کلاس قرار دهد و دستوراتی را برای افزودن، حذف و کنترل دانلودها به سرویس ارسال کند. نمودار زیر کلاس های اصلی را نشان می دهد که درگیر هستند.
-
DownloadService
: یکDownloadManager
را می پیچد و دستورات را به آن ارسال می کند. این سرویس بهDownloadManager
اجازه می دهد حتی زمانی که برنامه در پس زمینه است به کار خود ادامه دهد. -
DownloadManager
: دانلودهای متعدد را مدیریت می کند، حالات آنها را از (و به)DownloadIndex
بارگیری (و ذخیره) می کند، دانلودها را بر اساس الزاماتی مانند اتصال به شبکه و غیره شروع و متوقف می کند. برای دانلود محتوا، مدیر معمولاً دادههای در حال دانلود را ازHttpDataSource
میخواند و آن را درCache
مینویسد. -
DownloadIndex
: وضعیت دانلودها را حفظ می کند.
ایجاد سرویس دانلود
برای ایجاد یک DownloadService
، آن را زیر کلاس بندی کنید و متدهای انتزاعی آن را پیاده سازی کنید:
-
getDownloadManager()
:DownloadManager
مورد استفاده را برمیگرداند. -
getScheduler()
: یکScheduler
اختیاری را برمیگرداند که میتواند در صورت برآورده شدن الزامات مورد نیاز برای دانلودهای معلق برای پیشرفت، سرویس را مجدداً راهاندازی کند. ExoPlayer این پیاده سازی ها را ارائه می دهد:-
PlatformScheduler
، که از JobScheduler استفاده می کند (حداقل API 21 است). برای اطلاع از الزامات مجوز برنامه به PlatformScheduler javadocs مراجعه کنید. -
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
شما برگردانده شود:
کاتلین
// 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
جاوا
// 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
استفاده کنید . مثال زیر نحوه ایجاد درخواست دانلود را نشان می دهد:
کاتلین
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
جاوا
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
در این مثال، contentId
یک شناسه منحصر به فرد برای محتوا است. در موارد ساده، contentUri
اغلب میتواند بهعنوان contentId
استفاده شود، با این حال برنامهها میتوانند از هر طرح شناسهای که به بهترین وجه مناسب مورد استفادهشان است استفاده کنند. DownloadRequest.Builder
همچنین دارای تعدادی تنظیم کننده اختیاری است. به عنوان مثال، setKeySetId
و setData
را می توان برای تنظیم DRM و داده های سفارشی که برنامه می خواهد به ترتیب با دانلود مرتبط کند، استفاده شود. نوع MIME محتوا را نیز می توان با استفاده از setMimeType
تعیین کرد، به عنوان راهنمایی برای مواردی که نمی توان نوع محتوا را از contentUri
استنباط کرد.
پس از ایجاد، درخواست را می توان به DownloadService
برای افزودن دانلود ارسال کرد:
کاتلین
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
جاوا
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
در این مثال، MyDownloadService
زیرکلاس DownloadService
برنامه است و پارامتر foreground
کنترل میکند که آیا سرویس در پیشزمینه شروع میشود یا خیر. اگر برنامه شما از قبل در پیش زمینه است، پارامتر foreground
معمولاً باید روی false
تنظیم شود زیرا اگر DownloadService
تشخیص دهد که کاری برای انجام دادن دارد، خود را در پیش زمینه قرار می دهد.
حذف دانلودها
یک دانلود را می توان با ارسال فرمان حذف به DownloadService
حذف کرد، جایی که contentId
دانلودی را که باید حذف شود را شناسایی می کند:
کاتلین
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
جاوا
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
همچنین میتوانید همه دادههای دانلود شده را با DownloadService.sendRemoveAllDownloads
حذف کنید.
شروع و توقف دانلودها
دانلود فقط در صورتی پیشرفت می کند که چهار شرط وجود داشته باشد:
- دانلود دلیل توقف ندارد.
- دانلودها متوقف نمی شوند.
- الزامات برای دانلودها برای پیشرفت برآورده شده است. الزامات میتواند محدودیتهایی را در انواع شبکه مجاز مشخص کند، همچنین اینکه آیا دستگاه باید بیحرکت باشد یا به شارژر متصل باشد.
- از حداکثر تعداد دانلودهای موازی تجاوز نمی شود.
همه این شرایط را می توان با ارسال دستورات به DownloadService
کنترل کرد.
تنظیم و پاک کردن دلایل توقف دانلود
ممکن است دلیلی برای توقف یک یا همه دانلودها تعیین کنید:
کاتلین
// 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 )
جاوا
// 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
باقی میمانند، و اگر فرآیند برنامه از بین برود و بعداً دوباره راهاندازی شود، حفظ میشوند.
مکث و از سرگیری همه دانلودها
همه دانلودها را می توان به صورت زیر متوقف و از سر گرفت:
کاتلین
// Pause all downloads. DownloadService.sendPauseDownloads( context, MyDownloadService::class.java, /* foreground= */ false ) // Resume all downloads. DownloadService.sendResumeDownloads( context, MyDownloadService::class.java, /* foreground= */ false )
جاوا
// 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
تغییر داد:
کاتلین
// Set the download requirements. DownloadService.sendSetRequirements( context, MyDownloadService::class.java, requirements, /* foreground= */ false)
جاوا
// 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
اضافه کنید تا از تغییر وضعیت دانلودهای فعلی مطلع شوید:
کاتلین
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
جاوا
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
برای مثال ملموس DownloadManagerListener
در کلاس DownloadTracker
برنامه آزمایشی ببینید.
پخش محتوای دانلود شده
پخش محتوای دانلود شده مشابه پخش محتوای آنلاین است، با این تفاوت که داده ها به جای خواندن از طریق شبکه، از Cache
بارگیری خوانده می شوند.
برای پخش محتوای دانلود شده، یک CacheDataSource.Factory
با استفاده از همان نمونه Cache
که برای دانلود استفاده شد ایجاد کنید و هنگام ساخت پخش کننده، آن را به DefaultMediaSourceFactory
تزریق کنید:
کاتلین
// 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()
جاوا
// 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
جداگانه در دسترس قرار دهید، که میتواند مستقیماً به پخشکننده ارسال شود:
کاتلین
val mediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)) player.setMediaSource(mediaSource) player.prepare()
جاوا
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)); player.setMediaSource(mediaSource); player.prepare();
دانلود و پخش جریان های تطبیقی
جریانهای تطبیقی (مانند DASH، SmoothStreaming و HLS) معمولاً حاوی چندین آهنگ رسانه هستند. اغلب چندین آهنگ وجود دارد که حاوی محتوای یکسان با کیفیت های مختلف است (به عنوان مثال آهنگ های ویدئویی SD، HD و 4K). همچنین ممکن است چندین آهنگ از یک نوع حاوی محتوای متفاوت وجود داشته باشد (مثلاً چندین آهنگ صوتی به زبانهای مختلف).
برای پخش جریانی، میتوان از یک انتخابگر آهنگ برای انتخاب آهنگها استفاده کرد. به طور مشابه، برای دانلود، می توان از یک DownloadHelper
استفاده کرد تا انتخاب کند کدام یک از آهنگ ها دانلود شوند. استفاده معمولی از DownloadHelper
این مراحل را دنبال می کند:
- با استفاده از یکی از متدهای
DownloadHelper.forMediaItem
یکDownloadHelper
بسازید. کمک کننده را آماده کنید و منتظر تماس باشید.کاتلین
val downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), DefaultRenderersFactory(context), dataSourceFactory ) downloadHelper.prepare(callback)
جاوا
DownloadHelper downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), new DefaultRenderersFactory(context), dataSourceFactory); downloadHelper.prepare(callback);
- به صورت اختیاری، آهنگهای انتخابی پیشفرض را با استفاده از
getMappedTrackInfo
وgetTrackSelections
بررسی کنید و با استفاده ازclearTrackSelections
،replaceTrackSelections
وaddTrackSelection
تنظیمات را انجام دهید. - با فراخوانی
getDownloadRequest
یکDownloadRequest
برای آهنگ های انتخاب شده ایجاد کنید. همانطور که در بالا توضیح داده شد، می توان درخواست را برای افزودن دانلود بهDownloadService
ارسال کرد. - کمک کننده را با استفاده از
release()
رها کنید.
پخش محتوای تطبیقی دانلود شده به پیکربندی پخشکننده و ارسال MediaItem
مربوطه، همانطور که در بالا توضیح داده شد، نیاز دارد.
هنگام ساخت MediaItem
، MediaItem.localConfiguration.streamKeys
باید طوری تنظیم شود که در DownloadRequest
مطابقت داشته باشد تا پخش کننده فقط سعی کند زیرمجموعه آهنگ های دانلود شده را پخش کند. استفاده از Download.request.toMediaItem
و DownloadRequest.toMediaItem
برای ساخت MediaItem
این کار را برای شما انجام خواهد داد.