Medya indiriliyor

ExoPlayer, çevrimdışı oynatma için medya indirme işlevi sağlar. Çoğu kullanım alanında, uygulamanız arka plandayken bile indirmelerin devam etmesi istenir. Bu kullanım alanlarında uygulamanız DownloadService alt sınıfını olmalı ve indirmeleri eklemek, kaldırmak ve kontrol etmek için hizmete komutlar göndermelidir. Aşağıdaki şemada, dahil olan ana sınıflar gösterilmektedir.

Medya indirmek için dersler. Ok yönleri, veri akışını gösterir.

  • DownloadService: Bir DownloadManager'ı sarmalayarak komutları ona iletir. Bu hizmet, uygulama arka plandayken bile DownloadManager'ün çalışmaya devam etmesine olanak tanır.
  • DownloadManager: Birden fazla indirme işlemini yönetir, indirme işlemlerinin durumlarını bir DownloadIndex'dan (veya DownloadIndex'a) yükler (ve depolar), indirme işlemlerini ağ bağlantısı gibi koşullara göre başlatır ve durdurur. Yönetici, içeriği indirmek için genellikle HttpDataSource'ten indirilen verileri okuyup Cache'a yazar.
  • DownloadIndex: İndirmelerin durumlarını korur.

DownloadService oluşturma

Bir DownloadService oluşturmak için sınıfını alt sınıfa alın ve soyut yöntemlerini uygulayın:

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

Son olarak, AndroidManifest.xml dosyanızda hizmeti 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 sürümlerine göz atın.

İndirme Yöneticisi Oluşturma

Aşağıdaki kod snippet'inde, DownloadService içinde getDownloadManager() tarafından döndürülebilecek bir DownloadManager öğesinin nasıl oluşturulacağı gösterilmektedir:

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 bir DownloadRequest oluşturun ve DownloadService'inize gönderin. Uyarlanabilir akışlarda DownloadRequest oluşturulmasına yardımcı olmak için DownloadHelper özelliğini 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çeriğin benzersiz tanımlayıcısıdır. Basit durumlarda contentUri genellikle contentId olarak kullanılabilir ancak uygulamalar, kullanım alanlarına en uygun kimlik şemasını kullanmakta serbesttir. DownloadRequest.Builder'te bazı isteğe bağlı ayarlayıcılar da vardır. Örneğin, setKeySetId ve setData, DRM'yi ve uygulamanın indirme işlemiyle ilişkilendirmek istediği özel verileri sırasıyla ayarlamak için kullanılabilir. İçeriğin MIME türü, içerik türünün contentUri'ten çıkarılamadığı durumlarda ipucu olarak setMimeType kullanılarak da belirtilebilir.

Oluşturulan istek, indirme işlemini eklemek için DownloadService'e 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 DownloadService, yapması gereken bir iş olduğunu belirlerse kendisini ön plana koyacağından foreground parametresi normalde false olarak ayarlanmalıdır.

İndirilenler kaldırılıyor

Bir indirme, DownloadService adresine kaldırma komutu gönderilerek kaldırılabilir. Bu komutta contentId, kaldırılacak indirme işlemini tanımlar:

Kotlin

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

Java

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

Ayrıca, indirilen tüm verileri DownloadService.sendRemoveAllDownloads ile kaldırabilirsiniz.

İndirme işlemlerini başlatma ve durdurma

İndirme işlemi yalnızca dört koşul karşılanırsa devam eder:

  • İndirme işleminin duraklatma nedeni yok.
  • İndirmeler duraklatılmaz.
  • Devam etmek için indirme koşulları karşılanır. Şartlar, izin verilen ağ türleriyle ilgili kısıtlamaların yanı sıra cihazın boşta mı yoksa bir şarj cihazına bağlı mı olması gerektiğini belirtebilir.
  • Maksimum paralel indirme sayısı aşılmaz.

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

İndirme işleminin durdurulma nedenlerini ayarlama ve temizleme

Bir veya tüm indirme işlemlerinin durdurulmasına neden olan durumu ayarlayabilirsiniz:

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, indirme işleminin durdurulmadığı anlamına gelen özel bir değerdir). İndirmelerin durdurulmasının birden fazla nedeni olan uygulamalar, her indirmenin neden durdurulduğunu takip etmek için farklı değerler kullanabilir. Tüm indirme işlemlerinin durdurma nedenini ayarlama ve temizleme işlemi, tek bir indirme işleminin durdurma nedenini ayarlama ve temizleme işlemiyle aynı şekilde çalışır. Tek fark, contentId parametresinin null olarak ayarlanmasıdır.

Durdurma nedeni sıfır olmayan bir indirme Download.STATE_STOPPED durumundadır. Durdurma nedenleri DownloadIndex içinde kalır ve bu nedenle, uygulama işlemi sonlandırılıp daha sonra yeniden başlatılırsa saklanır.

Tüm indirmeler duraklatılıyor ve devam ettiriliyor

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

Duraklatılan indirmelerin durumu Download.STATE_QUEUED olur. Durma nedenlerini ayarlama yönteminin aksine bu yaklaşımda durum değişiklikleri devam etmez. Yalnızca DownloadManager öğesinin çalışma zamanı durumunu etkiler.

İndirmelerin devam etmesi için gereksinimleri ayarlama

Requirements, indirme işleminin devam edebilmesi için karşılanması gereken kısıtlamaları belirtmek için kullanılabilir. Koşullar, yukarıdaki örnekte olduğu gibi DownloadManager oluşturulurken DownloadManager.setRequirements() çağırılarak ayarlanabilir. Ayrıca DownloadService'ye komut göndererek dinamik olarak da değiştirilebilir:

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

Gerekli koşullar karşılanmadığı için indirme işlemi devam edemiyorsa Download.STATE_QUEUED durumunda olur. DownloadManager.getNotMetRequirements() ile karşılanmayan koşulları sorgulayabilirsiniz.

Maksimum paralel indirme sayısını ayarlama

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

Devam eden maksimum paralel indirme işlemi olduğu için indirme işlemi devam edemezse Download.STATE_QUEUED durumunda olur.

İndirilenleri sorgulama

Bir DownloadManager'ın DownloadIndex özelliği, tamamlanan veya başarısız olanlar da dahil olmak üzere tüm indirme işlemlerinin durumu için sorgulanabilir. DownloadIndex, DownloadManager.getDownloadIndex() numarası arandığında elde edilebilir. Tüm indirmelerde iterasyon yapan bir imleç, daha sonra DownloadIndex.getDownloads() çağrısı yapılarak elde edilebilir. Alternatif olarak, tek bir indirme işleminin durumu DownloadIndex.getDownload() çağrısı yapılarak sorgulanabilir.

DownloadManager, yalnızca mevcut (ör. tamamlanmamış veya başarısız) indirmelerin durumunu döndüren DownloadManager.getCurrentDownloads() değerini de sağlar. Bu yöntem, mevcut 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.

İndirilenleri dinleme

Mevcut indirmelerin durumu değiştiğinde bilgi almak için DownloadManager uygulamasına 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 öğesine bakın.

İndirilen içeriği oynatma

İndirilen içeriğin oynatılması, internetteki içeriğin oynatılmasına benzer. Tek fark, verilerin ağ üzerinden değil, indirilen içerikten Cache okunmasıdır.

İ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 bu örneği 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çerikleri oynatmak için de kullanılacaksa oynatma sırasında bu 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çeriğe oynatma için erişebilir. Bu durumda, indirilen bir içeriği oynatmak, ilgili MediaItem öğesini oynatıcıya iletmek kadar basittir. MediaItem, Download.request.toMediaItem kullanılarak bir Download'dan veya DownloadRequest.toMediaItem kullanılarak doğrudan bir DownloadRequest'den elde edilebilir.

MediaSource yapılandırması

Önceki örnekte, indirme önbelleği tüm MediaItem'lerin oynatılması için kullanılabilir hale getirilmiştir. İndirme önbelleğini, doğrudan oynatıcıya iletilebilecek 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) genellikle birden fazla medya parçası içerir. Genellikle aynı içeriği farklı kalitede (ör. SD, HD ve 4K video parçaları) barındıran birden fazla parça bulunur. Aynı türde farklı içerikler içeren birden fazla parça da olabilir (ör. farklı dillerde birden fazla ses parçası).

Akış oynatmalarında, hangi parçaların çalınacağını seçmek için bir parça seçici kullanılabilir. Benzer şekilde, indirme işlemi sırasında hangi parçaların indirileceğini seçmek için bir DownloadHelper kullanılabilir. DownloadHelper tipik kullanımı aşağıdaki adımları izler:

  1. DownloadHelper.forMediaItem yöntemlerinden birini kullanarak bir DownloadHelper oluşturun. Yardımcıyı hazırlayın ve geri aramayı bekleyin.

    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);
    
  2. İsteğe bağlı olarak, getMappedTrackInfo ve getTrackSelections tuşlarını kullanarak varsayılan olarak seçili parçaları inceleyebilir ve clearTrackSelections, replaceTrackSelections ve addTrackSelection tuşlarını kullanarak düzenlemeler yapabilirsiniz.
  3. getDownloadRequest işlevini çağırarak seçili parçalar için bir DownloadRequest oluşturun. Yukarıda açıklandığı şekilde, indirme işlemini eklemek için istek DownloadService'inize iletilebilir.
  4. release() simgesini kullanarak yardımcıyı serbest bırakın.

İndirilen uyarlanabilir içeriğin oynatılabilmesi için oynatıcının yapılandırılması ve yukarıda açıklandığı gibi ilgili MediaItem öğesinin iletilmesi gerekir.

MediaItem oluşturulurken MediaItem.localConfiguration.streamKeys, oynatıcının yalnızca indirilen parça alt kümesini çalmaya çalışması için DownloadRequest'dekilerle eşleşecek şekilde ayarlanmalıdır. MediaItem oluşturmak için Download.request.toMediaItem ve DownloadRequest.toMediaItem'ü kullandığınızda bu işlem sizin için yapılır.