Medya indiriliyor

ExoPlayer, çevrimdışı oynatma için medya indirme işlevi sağlar. Çoğu kullanım durumunda, uygulamanız arka planda olsa bile indirmelerin devam etmesi istenir. Bu kullanım durumları için uygulamanızın DownloadService alt sınıfını oluşturması ve indirmeleri eklemek, kaldırmak ve kontrol etmek için hizmete komutlar göndermesi gerekir. Aşağıdaki diyagramda söz konusu olan ana sınıflar gösterilmektedir.

Medya indirme dersleri. Ok yönleri veri akışını göstermektedir.

  • DownloadService: DownloadManager öğesini sarar ve komutları ona iletir. Hizmet, uygulama arka planda olsa bile DownloadManager'ın çalışmaya devam etmesini sağlar.
  • DownloadManager: Birden fazla indirmeyi yönetir, durumlarını bir yerden (ve bir yere) yükler (ve depolar)DownloadIndex , ağ bağlantısı gibi gereksinimlere bağlı olarak indirmeleri başlatma ve durdurma. İçeriği indirmek için yönetici genellikle HttpDataSource konumundan indirilen verileri okur ve Cache konumuna yazar.
  • DownloadIndex: İndirmelerin durumlarını kalıcı hale getirir.

Bir İndirme Hizmeti Oluşturma

DownloadService oluşturmak için onu alt sınıfa ayırın ve soyut yöntemlerini uygulayın:

  • getDownloadManager(): Kullanılacak DownloadManager değerini döndürür.
  • getScheduler(): Bekleyen indirmelerin ilerlemesi için gereken gereksinimler karşılandığında hizmeti yeniden başlatabilen isteğe bağlı bir Scheduler döndürür. ExoPlayer şu uygulamaları sağlar:
  • getForegroundNotification(): Hizmet ön planda çalışırken görüntülenecek bir bildirim döndürür. Varsayılan stilde bir bildirim oluşturmak için DownloadNotificationHelper.buildProgressNotification kullanabilirsiniz.

Son olarak, hizmeti AndroidManifest.xml dosyanızda tanımlayın:

<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>

Somut bir örnek için ExoPlayer demo uygulamasındaki DemoDownloadService ve AndroidManifest.xml öğelerine bakın.

DownloadManager oluşturma

Aşağıdaki kod snippet'inde, DownloadManager öğesinin nasıl oluşturulacağı gösterilmektedir. Bu öğe, DownloadService öğenizdeki getDownloadManager() tarafından döndürülebilir:

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);

Somut bir örnek için demo uygulamasındaki DemoUtil bölümüne bakın.

İndirme ekleme

İndirme eklemek için DownloadRequest oluşturup DownloadService adresinize gönderin. Uyarlanabilir akışlar için bir DownloadRequest oluşturmaya yardımcı olmak için DownloadHelper kullanın. Aşağıdaki örnekte indirme isteğinin nasıl oluşturulacağı gösterilmektedir:

Kotlin

val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()

Java

DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();

Bu örnekte, contentId içerik için benzersiz bir tanımlayıcıdır. Basit durumlarda, contentUri genellikle contentId olarak kullanılabilir, ancak uygulamalar kendi kullanım durumlarına en uygun kimlik şemasını kullanmakta özgürdür. DownloadRequest.Builder'ın ayrıca bazı isteğe bağlı ayarlayıcıları da vardır. Örneğin, setKeySetId ve setData sırasıyla uygulamanın indirmeyle ilişkilendirmek istediği DRM ve özel verileri ayarlamak için kullanılabilir. İçeriğin MIME türü, içerik türünün contentUri öğesinden çıkarılamadığı durumlar için bir ipucu olarak setMimeType kullanılarak da belirtilebilir.

Oluşturulduktan sonra, indirmeyi eklemek için istek DownloadService adresine gönderilebilir:

Kotlin

DownloadService.sendAddDownload(
  context,
  MyDownloadService::class.java,
  downloadRequest,
  /* foreground= */ false
)

Java

DownloadService.sendAddDownload(
    context, MyDownloadService.class, downloadRequest, /* foreground= */ false);

Bu örnekte, MyDownloadService uygulamanın DownloadService alt sınıfıdır ve foreground parametresi, hizmetin ön planda başlatılıp başlatılmayacağını kontrol eder. Uygulamanız zaten ön plandaysa foreground parametresi normalde false olarak ayarlanmalıdır. Çünkü DownloadService, yapması gereken bir iş olduğunu belirlerse kendisini ön plana çıkarır.

İndirilenler kaldırılıyor

İndirme, DownloadService adresine bir kaldırma komutu gönderilerek kaldırılabilir. contentId, kaldırılacak indirmeyi tanımlar:

Kotlin

DownloadService.sendRemoveDownload(
  context,
  MyDownloadService::class.java,
  contentId,
  /* foreground= */ false
)

Java

DownloadService.sendRemoveDownload(
    context, MyDownloadService.class, contentId, /* foreground= */ false);

İndirilen tüm verileri DownloadService.sendRemoveAllDownloads ile de kaldırabilirsiniz.

İndirme işlemlerini başlatma ve durdurma

İndirme işleminin devam edebilmesi için dört koşulun karşılanması gerekir:

  • İndirme işleminin durdurulma nedeni yok.
  • İndirmeler duraklatılmamış olmalıdır.
  • İndirmelerin devam edebilmesi için gerekli şartlar sağlandı. Gereksinimler, izin verilen ağ türleri üzerindeki kısıtlamaları ve cihazın boşta mı yoksa şarj cihazına bağlı mı olması gerektiğini belirleyebilir.
  • Maksimum paralel indirme sayısı aşılmamalıdır.

Bu koşulların tümü, DownloadService cihazınıza komut gönderilerek kontrol edilebilir.

İndirme durdurma nedenlerini ayarlama ve temizleme

Bir veya tüm indirmelerin durdurulması için bir neden belirlemek mümkündür:

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 sıfır olmayan herhangi bir değer olabilir (Download.STOP_REASON_NONE = 0, indirmenin durdurulmadığı anlamına gelen özel bir değerdir). İndirmeleri durdurmak için birden fazla nedeni olan uygulamalar, her indirmenin neden durdurulduğunu takip etmek için farklı değerler kullanabilir. Tüm indirmeler için durdurma nedenini ayarlama ve temizleme, tek bir indirme için durdurma nedenini ayarlama ve temizleme ile aynı şekilde çalışır; tek fark, contentId değerinin null olarak ayarlanması gerektiğidir.

Bir indirmenin sıfır olmayan bir durdurma nedeni varsa, Download.STATE_STOPPED durumunda olacaktır. Durdurma nedenleri DownloadIndex içinde kalıcı olarak saklanır. Bu nedenle, uygulama işlemi sonlandırılıp daha sonra yeniden başlatılırsa durdurma nedenleri korunur.

Tüm indirmeleri duraklatma ve devam ettirme

Tüm indirmeler aşağıdaki şekilde duraklatılabilir ve devam ettirilebilir:

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);

İndirmeler duraklatıldığında Download.STATE_QUEUED durumunda olacaklar. Durdurma nedenlerini ayarlamanın aksine, bu yaklaşım hiçbir durum değişikliğini kalıcı hale getirmez. Bu yalnızca DownloadManager çalışma zamanı durumunu etkiler.

İndirmelerin ilerlemesi için gereksinimlerin ayarlanması

Requirements, indirme işleminin devam etmesi için karşılanması gereken kısıtlamaları belirtmek üzere kullanılabilir. Şartlar, DownloadManager oluşturulurken DownloadManager.setRequirements() çağrılarak ayarlanabilir. Bu işlem, yukarıdaki örnekte gösterilmiştir. Ayrıca DownloadService komutu gönderilerek dinamik olarak da değiştirilebilirler:

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);

Gereksinimler karşılanmadığı için indirme işlemi devam edemediğinde Download.STATE_QUEUED durumunda olur. Karşılanmayan gereksinimleri DownloadManager.getNotMetRequirements() ile sorgulayabilirsiniz.

Maksimum paralel indirme sayısını ayarlama

Maksimum paralel indirme sayısı, DownloadManager.setMaxParallelDownloads() çağrılarak ayarlanabilir. Bu işlem normalde yukarıdaki örnekte olduğu gibi DownloadManager oluşturulurken yapılır.

Maksimum sayıda paralel indirme işlemi zaten devam ettiği için indirme işlemi devam edemediğinde Download.STATE_QUEUED durumunda olur.

İndirmeleri sorgulama

DownloadManager öğesinin DownloadIndex öğesi, tamamlanan veya başarısız olanlar dahil olmak üzere tüm indirmelerin durumu için sorgulanabilir. DownloadIndex, DownloadManager.getDownloadIndex() çağrılarak elde edilebilir. Daha sonra DownloadIndex.getDownloads() çağrılarak tüm indirmeler üzerinde yineleme yapan bir imleç elde edilebilir. Alternatif olarak, tek bir indirmenin durumu DownloadIndex.getDownload() çağrılarak sorgulanabilir.

DownloadManager ayrıca yalnızca geçerli (yani tamamlanmamış veya başarısız) indirmelerin durumunu döndüren DownloadManager.getCurrentDownloads() öğesini de sağlar. Bu yöntem, devam eden indirme işlemlerinin ilerleme durumunu ve durumunu gösteren bildirimleri ve diğer kullanıcı arayüzü bileşenlerini güncellemek için kullanışlıdır.

İndirmeleri dinleme

Mevcut indirmelerin durumu değiştiğinde bilgilendirilmek için DownloadManager'a bir dinleyici ekleyebilirsiniz:

Kotlin

downloadManager.addListener(
  object : DownloadManager.Listener { // Override methods of interest here.
  }
)

Java

downloadManager.addListener(
    new DownloadManager.Listener() {
      // Override methods of interest here.
    });

Somut bir örnek için demo uygulamanın DownloadTracker sınıfındaki DownloadManagerListener bölümüne bakın.

İndirilen içerik oynatılıyor

İndirilen içerikleri oynatmak, ağ üzerinden değil, indirme işleminden Cache veri okunması dışında online içerikleri oynatmaya benzer.

İndirilen içeriği oynatmak için, indirme için kullanılan Cache örneğini kullanarak bir CacheDataSource.Factory oluşturun ve oynatıcıyı oluştururken bunu DefaultMediaSourceFactory içine ekleyin:

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();

Aynı oynatıcı örneği indirilmemiş içeriği oynatmak için de kullanılacaksa, oynatma sırasında söz konusu içeriğin de indirilmesini önlemek için CacheDataSource.Factory salt okunur olarak yapılandırılmalıdır.

Oynatıcı CacheDataSource.Factory ile yapılandırıldıktan sonra, indirilen içeriklere oynatma için erişebilir. İndirilen içeriği oynatmak için ilgili MediaItem öğesini oyuncuya iletmeniz yeterlidir. MediaItem, Download.request.toMediaItem kullanılarak Download üzerinden veya DownloadRequest.toMediaItem kullanılarak doğrudan DownloadRequest üzerinden alınabilir.

MediaSource yapılandırması

Önceki örnekte, indirme önbelleği tüm MediaItem'ların oynatılması için kullanılabilir hale getiriliyor. Ayrıca indirme önbelleğini, doğrudan oynatıcıya iletilebilen tek tek MediaSource örnekleri için de kullanılabilir hale getirebilirsiniz:

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();

Uyarlanabilir akışları indirme ve oynatma

Uyarlanabilir akışlar (ör. DASH, SmoothStreaming ve HLS) normalde birden fazla medya parçası içerir. Genellikle farklı kalitelerde (ör. SD, HD ve 4K video parçaları) aynı içeriği içeren birden fazla parça bulunur. Aynı türde farklı içerikler barındıran birden fazla parça da olabilir (örneğin farklı dillerde birden fazla ses parçası).

Akışlı oynatmalar için, hangi parçaların çalınacağını seçmek amacıyla bir parça seçici kullanılabilir. Benzer şekilde, indirme işleminde hangi parçaların indirileceğini seçmek için DownloadHelper kullanılabilir. DownloadHelper'nin tipik kullanımı şu adımları takip eder:

  1. DownloadHelper.Factory örneğini kullanarak DownloadHelper oluşturun. Yardımcıyı hazırlayın ve geri aramayı bekleyin.

    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);
  2. İsteğe bağlı olarak, getMappedTrackInfo ve getTrackSelections simgelerini kullanarak varsayılan olarak seçilen parçaları inceleyin ve clearTrackSelections, replaceTrackSelections ve addTrackSelection simgelerini kullanarak düzenlemeler yapın.
  3. Seçili parçalar için getDownloadRequest çağırarak bir DownloadRequest oluşturun. Yukarıda açıklandığı gibi, indirmeyi eklemek için istek DownloadService'nize iletilebilir.
  4. release() kullanarak yardımcıyı serbest bırakın.

İndirilen uyarlanabilir içeriğin oynatılması, oynatıcının yapılandırılmasını ve yukarıda açıklandığı gibi ilgili MediaItem değerinin iletilmesini gerektirir.

MediaItem oluşturulurken, oynatıcının yalnızca indirilen parçaların alt kümesini çalmaya çalışması için MediaItem.localConfiguration.streamKeys, DownloadRequest içindekilerle eşleşecek şekilde ayarlanmalıdır. Download.request.toMediaItem ve DownloadRequest.toMediaItem kullanarak MediaItem'yi oluşturmak bu işi sizin için halledecektir.