درحال بارگیری رسانه

‫ExoPlayer قابلیت بارگیری رسانه برای بازپخش آفلاین را فراهم می‌کند. در اکثر موارد استفاده، مطلوب است که بارگیری‌ها حتی زمانی که برنامه شما در پس‌زمینه است ادامه یابد. برای این موارد استفاده، برنامه شما باید زیرکلاس DownloadService باشد و برای افزودن، برداشتن، و کنترل بارگیری‌ها، فرمان‌هایی به سرویس ارسال کند. نمودار زیر کلاس‌های اصلی درگیر را نشان می‌دهد.

کلاس‌هایی برای بارگیری رسانه. جهت‌های پیکان نشان‌دهنده
      جریان داده‌ها است.
شکل ۱. کلاس‌هایی برای بارگیری رسانه. جهت‌های پیکان جریان داده‌ها را نشان می‌دهد.
  • ‫DownloadService: DownloadManager را می‌پیچد و فرمان‌ها را به آن ارسال می‌کند. این سرویس به DownloadManager امکان می‌دهد حتی وقتی برنامه در پس‌زمینه است به کار خود ادامه دهد.
  • DownloadManager: بارگیری‌های متعدد را مدیریت می‌کند، وضعیت آن‌ها را از (و به) DownloadIndex بار می‌کند (و ذخیره می‌کند)، و بارگیری‌ها را براساس الزامات، مانند اتصال‌پذیری شبکه، شروع و متوقف می‌کند. برای بارگیری محتوا، مدیر معمولاً داده‌های بارگیری‌شده را از HttpDataSource می‌خواند و آن‌ها را در Cache می‌نویسد.
  • DownloadIndex: وضعیت بارگیری‌ها را حفظ می‌کند.

درحال ایجاد DownloadService

برای ایجاد DownloadService، آن را زیرکلاس کنید و روش‌های انتزاعی آن را پیاده‌سازی کنید:

  • ‫getDownloadManager(): DownloadManager را برای استفاده برمی‌گرداند.
  • ‫getScheduler(): Scheduler اختیاری را برمی‌گرداند که می‌تواند وقتی الزامات لازم برای پیشرفت بارگیری‌های معلقه برآورده شد، سرویس را بازراه‌اندازی کند. ‫ExoPlayer این پیاده‌سازی‌ها را ارائه می‌دهد:
    • ‫PlatformScheduler که از JobScheduler استفاده می‌کند (حداقل API ‏ ۲۱ است). برای اطلاع از الزامات اجازه برنامه، 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

تکه‌کد زیر نشان می‌دهد چگونه 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 به‌عنوان راهنمایی برای مواردی که نوع MIME را نمی‌توان از 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 بارگیری‌شده خوانده می‌شود.

برای پخش محتوای بارگیری‌شده، بااستفاده از همان نمونه Cache که برای بارگیری استفاده شده است، CacheDataSource.Factory ایجاد کنید و هنگام ساختن پخش‌کننده، آن را به 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 این مراحل را دنبال می‌کند:

  1. بااستفاده از نمونه DownloadHelper.Factory، DownloadHelper بسازید. کمک‌کننده را آماده کنید و منتظر تماس برگشتی بمانید.
  2. به‌صورت اختیاری، قطعه‌های انتخاب‌شده پیش‌فرض را بااستفاده از getMappedTrackInfo و getTrackSelections بازرسی کنید و بااستفاده از clearTrackSelections، replaceTrackSelections، و addTrackSelection اصلاحات را انجام دهید.
  3. با فراخواندن getDownloadRequest، DownloadRequest برای قطعه‌های انتخاب‌شده ایجاد کنید. درخواست می‌تواند به DownloadService شما ارسال شود تا بارگیری را اضافه کند، همان‌طور که قبلاً توضیح داده شد.
  4. بااستفاده از release()، دستیار را رها کنید.

کاتلین

val downloadHelper =
  DownloadHelper.Factory()
    .setRenderersFactory(DefaultRenderersFactory(context))
    .setDataSourceFactory(dataSourceFactory)
    .create(MediaItem.fromUri(contentUri))
downloadHelper.prepare(callback)

جاوا

DownloadHelper downloadHelper =
    new DownloadHelper.Factory()
        .setRenderersFactory(new DefaultRenderersFactory(context))
        .setDataSourceFactory(dataSourceFactory)
        .create(MediaItem.fromUri(contentUri));
downloadHelper.prepare(callback);

بازپخش محتوای تطبیقی بارگیری‌شده نیازمند پیکربندی پخش‌کننده و گذراندن MediaItem مربوطه است، همان‌طور که قبلاً توضیح داده شد.

هنگام ساختن MediaItem، MediaItem.localConfiguration.streamKeys باید تنظیم شود تا با موارد موجود در DownloadRequest مطابقت داشته باشد تا پخش‌کننده فقط سعی کند زیرمجموعه قطعه‌هایی را که بارگیری شده‌اند پخش کند. استفاده از Download.request.toMediaItem و DownloadRequest.toMediaItem برای ساختن MediaItem این کار را برای شما انجام خواهد داد.