Medya indiriliyor

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

Medya indirme sınıfları. Ok yönleri veri akışını gösterir.

  • DownloadService: Bir DownloadManager öğesini sarmalar ve komutları buna yönlendirir. Bu hizmet, uygulama arka plandayken bile DownloadManager hizmetinin çalışmaya devam etmesini sağlar.
  • DownloadManager: Birden çok indirme işlemini, durumlarını DownloadIndex ile yükleme (ve depolama) ve indirme işlemlerini ağ bağlantısı gibi gerekliliklere göre başlatıp durdurma işlemlerini yönetir. Yönetici, içeriği indirmek için genellikle bir HttpDataSource ağından indirilen verileri okur ve bir Cache içine yazar.
  • DownloadIndex: İndirmelerin durumlarını korur.

İndirme Hizmeti Oluşturma

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

  • getDownloadManager(): Kullanılacak DownloadManager değerini döndürür.
  • getScheduler(): Bekleyen indirmelerin ilerlemesi için gerekli koşullar karşılandığında hizmeti yeniden başlatabilen 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 tarzda bir 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ında DemoDownloadService ve AndroidManifest.xml konularına bakın.

İndirme Yöneticisi oluşturma

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

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ında DemoUtil adresine bakın.

İndirilen bir dosyayı ekleme

İndirme eklemek için bir DownloadRequest oluşturup DownloadService cihazınıza gönderin. Uyarlanabilir akışlarda DownloadRequest oluşturulmasına yardımcı olmak için DownloadHelper kullanın. Aşağıdaki örnekte, bir 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, kullanım alanlarına en uygun kimlik şemasını kullanabilir. DownloadRequest.Builder öğesinde isteğe bağlı bazı belirleyiciler de vardır. Örneğin, setKeySetId ve setData, uygulamanın indirmeyle ilişkilendirmek istediği DRM'yi ve özel verileri sırasıyla ayarlamak için kullanılabilir. İçeriğin MIME türü, içerik türünün contentUri parametresinden çıkarılamadığı durumlar için ipucu olarak setMimeType kullanılarak da belirtilebilir.

İstek oluşturulduktan sonra, indirilen öğenin eklenmesi için DownloadService öğesine 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şin olduğunu belirlerse kendini ön plana koyar. Bu nedenle, foreground parametresinin normalde false olarak ayarlanması gerekir.

İndirilenler kaldırılıyor

İndirilen bir dosya DownloadService öğesine kaldır komutu gönderilerek kaldırılabilir. Burada 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 DownloadService.sendRemoveAllDownloads ile indirilen tüm verileri 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 bir durdurma nedeni yok.
  • İndirme işlemleri duraklatılmaz.
  • İndirme işleminin ilerleme durumu için gereksinimler karşılandı. Gereksinimler, cihazın boşta mı yoksa şarj cihazına bağlı mı olacağının yanı sıra izin verilen ağ türleriyle ilgili kısıtlamaları belirtebilir.
  • Maksimum paralel indirme sayısı aşılmadı.

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

İndirmeyi durdurma nedenlerini ayarlama ve temizleme

İndirmelerden birinin veya tüm indirmelerin durdurulmasına neden olacak 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 dışında herhangi bir değer olabilir (Download.STOP_REASON_NONE = 0, indirme işleminin durdurulmadığı anlamına gelen özel bir değerdir). İndirme işlemlerini durdurmak için birden çok nedeni olan uygulamalar, her bir indirme işleminin neden durdurulduğunu izlemek için farklı değerler kullanabilir. Tüm indirmelerin durdurma nedenini ayarlamak ve temizlemek, tek bir indirmenin durdurma nedenini belirlemek ve temizlemekle aynı şekilde işler. Ancak contentId, null olarak ayarlanmalıdır.

Sıfır olmayan bir durdurma nedeni olan indirmeler Download.STATE_STOPPED durumunda olur. Durdurma nedenleri DownloadIndex içinde kalıcı olarak saklanır ve uygulama işlemi sonlandırılıp daha sonra yeniden başlatılırsa da saklanır.

Tüm indirmeleri duraklatma ve devam ettirme

Tüm indirme işlemleri 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 olurlar. Durdurma nedenlerini ayarlamanın aksine bu yaklaşım herhangi bir durum değişikliğini korumaz. Yalnızca DownloadManager çalışma zamanı durumunu etkiler.

İlerleme durumu indirme gereksinimlerini ayarlama

Requirements, indirmelerin devam etmesi için karşılanması gereken kısıtlamaları belirtmek amacıyla kullanılabilir. Gereksinimler, yukarıdaki örnekte olduğu gibi, DownloadManager oluşturulurken DownloadManager.setRequirements() çağrısı yapılarak ayarlanabilir. Bunlar, DownloadService öğesine bir komut gönderilerek dinamik bir şekilde de 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);

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

Maksimum paralel indirme sayısı ayarlanıyor

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

Maksimum paralel indirme sayısı zaten devam ettiğinden indirme işlemi devam edemediğinde işlem Download.STATE_QUEUED durumunda olur.

İndirilenleri sorgulama

Bir DownloadManager öğesinin DownloadIndex öğesi, tamamlanmış veya başarısız olanlar da dahil olmak üzere tüm indirmelerin durumu için sorgulanabilir. DownloadIndex, DownloadManager.getDownloadIndex() çağrısı yapılarak elde edilebilir. Tüm indirmelerin üzerinde yinelenen bir imleç, DownloadIndex.getDownloads() çağrısıyla elde edilebilir. Alternatif olarak, tek bir indirmenin durumu DownloadIndex.getDownload() çağrısı yapılarak sorgulanabilir.

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

İndirilen içerikler dinleniyor

Mevcut indirmelerin durumu değiştiğinde bildirim almak için DownloadManager öğesine bir işleyici 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ı, çevrimiçi içeriğin oynatılmasına benzer; tek fark verilerin ağ üzerinden değil, indirilen öğeden Cache okunmasıdır.

İndirilen içeriği oynatmak için indirme işleminde 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();

İndirilmemiş içeriği oynatmak için de aynı oynatıcı örneği kullanılacaksa oynatma sırasında 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, oynatmak üzere indirilen içeriğe erişebilir. Böylelikle bir indirme işlemini yürütmek, ilgili MediaItem değerini oynatıcıya iletmek kadar basittir. Bir MediaItem, Download.request.toMediaItem kullanılarak Download yönteminden veya DownloadRequest.toMediaItem kullanılarak doğrudan bir DownloadRequest aracından alınabilir.

MediaSource yapılandırması

Yukarıdaki örnek, indirme önbelleğinin tüm MediaItem'lerin oynatılması için kullanılabilir olmasını sağlar. İndirme önbelleğini, doğrudan oynatıcıya geçirilebilecek ayrı 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 kanalı içerir. Genellikle aynı içeriği farklı kalitelerde (ör. SD, HD ve 4K video parçaları) içeren birden fazla parça bulunur. Ayrıca, farklı içeriklere sahip aynı türde 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 DownloadHelper kullanılabilir. Tipik bir DownloadHelper kullanımı için şu adımlar izlenir:

  1. DownloadHelper.forMediaItem yöntemlerinden birini kullanarak bir DownloadHelper oluşturun. Yardımcıyı hazırlayın ve geri çağırmayı 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 ile varsayılan olarak seçilen parçaları denetleyin ve clearTrackSelections, replaceTrackSelections ve addTrackSelection kullanarak düzenlemeler yapın.
  3. getDownloadRequest numarasını arayarak seçili parçalar için bir DownloadRequest oluşturun. İstek, yukarıda açıklandığı gibi, indirme işlemini eklemek için DownloadService cihazınıza iletilebilir.
  4. release() öğesini kullanarak yardımcıyı bırakın.

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

MediaItem oluşturulurken MediaItem.localConfiguration.streamKeys, DownloadRequest kapsamındakilerle eşleşecek şekilde ayarlanmalıdır. Böylece oynatıcı yalnızca indirilen parçaların alt kümesini oynatmaya çalışır. MediaItem oluşturmak için Download.request.toMediaItem ve DownloadRequest.toMediaItem kullanılması bu işi sizin yerinize halleder.