ExoPlayer มีฟังก์ชันการดาวน์โหลดสื่อเพื่อเล่นแบบออฟไลน์ ใน Use Case ส่วนใหญ่ แนะนำให้ดาวน์โหลดต่อไปแม้ว่าแอปจะทำงานอยู่เบื้องหลัง สำหรับกรณีการใช้งานเหล่านี้ แอปของคุณควรสร้างคลาสย่อยของ DownloadService
และส่งคําสั่งไปยังบริการเพื่อเพิ่ม นําออก และควบคุมการดาวน์โหลด แผนภาพต่อไปนี้แสดงคลาสหลักที่เกี่ยวข้อง
DownloadService
: ตัดDownloadManager
และส่งต่อคําสั่งไปยังDownloadManager
บริการนี้ช่วยให้DownloadManager
ทำงานต่อไปได้แม้ว่าแอปจะอยู่ในเบื้องหลังDownloadManager
: จัดการการดาวน์โหลดหลายรายการ โหลด (และจัดเก็บ) สถานะการดาวน์โหลดจาก (และไปยัง)DownloadIndex
, เริ่มและหยุดการดาวน์โหลดตามข้อกำหนด เช่น การเชื่อมต่อเครือข่าย และอื่นๆ หากต้องการดาวน์โหลดเนื้อหา โดยทั่วไปตัวจัดการจะอ่านข้อมูลที่ดาวน์โหลดจากHttpDataSource
และเขียนลงในCache
DownloadIndex
: เก็บสถานะการดาวน์โหลดไว้
การสร้าง DownloadService
หากต้องการสร้างDownloadService
ให้จัดประเภทย่อยและใช้เมธอดนามธรรม
ของโครงสร้างดังกล่าว
getDownloadManager()
: แสดงผลDownloadManager
เพื่อใช้getScheduler()
: แสดงผลScheduler
ที่ไม่บังคับ ซึ่งสามารถรีสตาร์ทบริการได้เมื่อมีคุณสมบัติตรงตามข้อกําหนดที่จําเป็นสําหรับการดาวน์โหลดที่รอดําเนินการ ExoPlayer มีการใช้งานต่อไปนี้PlatformScheduler
ซึ่งใช้ JobScheduler (API ขั้นต่ำคือ 21) ดูข้อกำหนดสิทธิ์ของแอปใน Javadoc ของ PlatformSchedulerWorkManagerScheduler
ซึ่งใช้ 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
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);
ดูตัวอย่างจริงได้ที่ DemoUtil
ในแอปเดโม
การเพิ่มการดาวน์โหลด
หากต้องการเพิ่มการดาวน์โหลด ให้สร้างDownloadRequest
แล้วส่งไปยัง
DownloadService
สำหรับสตรีมแบบปรับได้ ให้ใช้ DownloadHelper
เพื่อช่วยสร้าง DownloadRequest
ตัวอย่างต่อไปนี้แสดงวิธีสร้างคำขอดาวน์โหลด
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
ในตัวอย่างนี้ contentId
คือตัวระบุที่ไม่ซ้ำกันสำหรับเนื้อหา ในกรณีง่ายๆ contentUri
มักใช้เป็น contentId
ได้ แต่แอปสามารถใช้รูปแบบรหัสใดก็ได้ที่เหมาะกับกรณีการใช้งานมากที่สุด DownloadRequest.Builder
ยังมีตัวตั้งค่าที่ไม่บังคับบางรายการด้วย เช่น setKeySetId
และ setData
สามารถใช้เพื่อตั้งค่า DRM และข้อมูลที่กำหนดเองที่แอปต้องการเชื่อมโยงกับการดาวน์โหลดได้ นอกจากนี้ คุณยังระบุประเภท MIME ของเนื้อหาได้โดยใช้ setMimeType
เพื่อเป็นการบอกใบ้ในกรณีที่ไม่สามารถอนุมานประเภทเนื้อหาจาก contentUri
เมื่อสร้างแล้ว คุณจะส่งคำขอไปยัง DownloadService
เพื่อเพิ่มการดาวน์โหลดได้ ดังนี้
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
ในตัวอย่างนี้ MyDownloadService
คือคลาสย่อย DownloadService
ของแอป และพารามิเตอร์ foreground
จะควบคุมว่าจะเริ่มต้นบริการในเบื้องหน้าหรือไม่ หากแอปอยู่ในเบื้องหน้าอยู่แล้ว ปกติแล้วคุณควรตั้งค่าพารามิเตอร์ foreground
เป็น false
เนื่องจาก DownloadService
จะแสดงตัวเองในเบื้องหน้าหากพิจารณาว่ามีงานให้ทำ
กำลังนำรายการที่ดาวน์โหลดออก
คุณนำการดาวน์โหลดออกได้โดยส่งคำสั่งนำออกไปยัง DownloadService
โดยที่ contentId
จะระบุการดาวน์โหลดที่จะนำออก
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
นอกจากนี้คุณยังนำข้อมูลที่ดาวน์โหลดไว้ทั้งหมดออกได้ด้วย DownloadService.sendRemoveAllDownloads
การเริ่มและหยุดการดาวน์โหลด
การดาวน์โหลดจะดำเนินการต่อเมื่อเป็นไปตามเงื่อนไข 4 ข้อต่อไปนี้เท่านั้น
- การดาวน์โหลดไม่มีเหตุผลในการหยุด
- การดาวน์โหลดไม่หยุดชั่วคราว
- เป็นไปตามข้อกำหนดสำหรับการดาวน์โหลด ข้อกำหนดสามารถระบุข้อจำกัดเกี่ยวกับประเภทเครือข่ายที่อนุญาต รวมถึงระบุว่าอุปกรณ์ควรไม่มีการใช้งานหรือเชื่อมต่อกับที่ชาร์จ
- ไม่มีการดาวน์โหลดพร้อมกันเกินจำนวนสูงสุด
เงื่อนไขทั้งหมดนี้ควบคุมได้โดยการส่งคำสั่งไปยัง DownloadService
การตั้งค่าและการล้างเหตุผลที่การดาวน์โหลดหยุด
คุณตั้งค่าเหตุผลที่การดาวน์โหลดรายการเดียวหรือทั้งหมดหยุดลงได้ ดังนี้
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
จะเป็นค่าใดก็ได้ที่ไม่ใช่ 0 (Download.STOP_REASON_NONE = 0
คือค่าพิเศษซึ่งหมายความว่าการดาวน์โหลดจะไม่หยุดทำงาน) แอปที่มีสาเหตุหลายประการในการหยุดการดาวน์โหลดสามารถใช้ค่าที่แตกต่างกันเพื่อติดตามสาเหตุที่การดาวน์โหลดแต่ละรายการหยุดลง การตั้งค่าและการล้างเหตุผลในการหยุดการดาวน์โหลดทั้งหมดจะทำงานเหมือนกับการตั้งค่าและการล้างเหตุผลในการหยุดการดาวน์โหลดรายการเดียว ยกเว้นว่าควรตั้งค่า contentId
เป็น null
เมื่อการดาวน์โหลดมีเหตุผลในการหยุดที่ไม่ใช่ 0 การดาวน์โหลดจะอยู่ในสถานะ Download.STATE_STOPPED
ระบบจะเก็บเหตุผลในการหยุดไว้ใน DownloadIndex
ดังนั้นเหตุผลดังกล่าวจะยังคงอยู่หากกระบวนการสมัครถูกหยุดและเริ่มอีกครั้งในภายหลัง
การหยุดดาวน์โหลดทั้งหมดชั่วคราวแล้วกลับมาดำเนินการอีกครั้ง
คุณหยุดการดาวน์โหลดทั้งหมดชั่วคราวและกลับมาดาวน์โหลดต่อได้โดยทำดังนี้
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);
เมื่อหยุดการดาวน์โหลดชั่วคราว รายการที่ดาวน์โหลดจะอยู่ในสถานะ Download.STATE_QUEUED
ซึ่งแตกต่างจากการตั้งค่าเหตุผลในการหยุดตรงที่จะไม่มีการเก็บการเปลี่ยนแปลงสถานะใดๆ โดยจะมีผลต่อสถานะรันไทม์ของ DownloadManager
เท่านั้น
การตั้งค่าข้อกำหนดเพื่อให้การดาวน์โหลดมีความคืบหน้า
Requirements
ใช้เพื่อระบุข้อจำกัดที่ต้องปฏิบัติตามเพื่อให้การดาวน์โหลดดำเนินการต่อได้ คุณตั้งข้อกําหนดได้โดยเรียกใช้ DownloadManager.setRequirements()
เมื่อสร้าง DownloadManager
ตามตัวอย่างด้านบน นอกจากนี้ คุณยังเปลี่ยนค่าเหล่านี้แบบไดนามิกได้ด้วยการส่งคําสั่งไปยัง DownloadService
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);
เมื่อดาวน์โหลดไม่สำเร็จเนื่องจากไม่เป็นไปตามข้อกำหนด ระบบจะแสดงสถานะเป็น Download.STATE_QUEUED
คุณค้นหาข้อกำหนดที่ไม่เป็นไปตามเกณฑ์ได้โดยใช้ DownloadManager.getNotMetRequirements()
การตั้งค่าจำนวนการดาวน์โหลดสูงสุดพร้อมกัน
คุณตั้งค่าจำนวนการดาวน์โหลดพร้อมกันสูงสุดได้โดยเรียกใช้ DownloadManager.setMaxParallelDownloads()
ซึ่งโดยปกติแล้วจะทำเมื่อสร้าง DownloadManager
ดังตัวอย่างด้านบน
เมื่อดำเนินการดาวน์โหลดไม่ได้เนื่องจากการดาวน์โหลดพร้อมกันถึงจำนวนสูงสุดแล้ว การดาวน์โหลดจะอยู่ในสถานะ Download.STATE_QUEUED
การค้นหาการดาวน์โหลด
คุณสามารถค้นหาสถานะการดาวน์โหลดทั้งหมด รวมถึงการดาวน์โหลดที่เสร็จสมบูรณ์หรือไม่สำเร็จได้จาก DownloadIndex
ของ DownloadManager
DownloadIndex
รับ DownloadManager.getDownloadIndex()
ได้โดยโทรไปที่ DownloadManager.getDownloadIndex()
คุณจะรับเคอร์เซอร์ที่ทำซ้ำจากการดาวน์โหลดทั้งหมดได้ด้วยการเรียกใช้ DownloadIndex.getDownloads()
หรือคุณจะสอบถามสถานะของการดาวน์โหลดครั้งเดียวได้โดยโทรไปที่ DownloadIndex.getDownload()
DownloadManager
ยังมี DownloadManager.getCurrentDownloads()
ซึ่งจะแสดงสถานะของการดาวน์โหลดปัจจุบัน (เช่น ยังไม่เสร็จสมบูรณ์หรือล้มเหลว) เท่านั้น วิธีนี้มีประโยชน์สำหรับการอัปเดตการแจ้งเตือนและคอมโพเนนต์ UI อื่นๆ ที่แสดงความคืบหน้าและสถานะของการดาวน์โหลดปัจจุบัน
กำลังฟังรายการที่ดาวน์โหลด
คุณสามารถเพิ่ม Listener ลงใน DownloadManager
เพื่อรับแจ้งเมื่อการดาวน์โหลดปัจจุบันมีการเปลี่ยนแปลงสถานะได้โดยทำดังนี้
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
ดูตัวอย่างที่เป็นรูปธรรมได้ที่ DownloadManagerListener
ในคลาส DownloadTracker
ของแอปเดโม
การเล่นเนื้อหาที่ดาวน์โหลด
การเล่นเนื้อหาที่ดาวน์โหลดจะคล้ายกับการเล่นเนื้อหาออนไลน์ ยกเว้นว่าระบบจะอ่านข้อมูลจากCache
การดาวน์โหลดแทนการอ่านผ่านเครือข่าย
หากต้องการเล่นเนื้อหาที่ดาวน์โหลด ให้สร้าง CacheDataSource.Factory
โดยใช้อินสแตนซ์ Cache
เดียวกับที่ใช้สำหรับการดาวน์โหลด และแทรกเนื้อหาดังกล่าวลงใน DefaultMediaSourceFactory
เมื่อสร้างโปรแกรมเล่น โดยทำดังนี้
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();
หากจะใช้อินสแตนซ์เพลเยอร์เดียวกันเพื่อเล่นเนื้อหาที่ไม่ได้ดาวน์โหลดด้วย ก็ควรกำหนดค่า CacheDataSource.Factory
เป็นอ่านอย่างเดียวเพื่อหลีกเลี่ยงการดาวน์โหลดเนื้อหานั้นด้วยระหว่างการเล่น
เมื่อกำหนดค่าโปรแกรมเล่นด้วย CacheDataSource.Factory
แล้ว โปรแกรมเล่นจะมีสิทธิ์เข้าถึงเนื้อหาที่ดาวน์โหลดไว้เพื่อเล่น จากนั้นการเล่นไฟล์ที่ดาวน์โหลดมาก็ง่ายเพียงส่ง MediaItem
ที่เกี่ยวข้องไปยังโปรแกรมเล่น MediaItem
สามารถรับได้จาก Download
โดยใช้ Download.request.toMediaItem
หรือรับจาก DownloadRequest
โดยตรงโดยใช้ DownloadRequest.toMediaItem
การกำหนดค่า MediaSource
ตัวอย่างก่อนหน้านี้ทำให้แคชดาวน์โหลดพร้อมใช้งานสำหรับการเล่น MediaItem
ทั้งหมด นอกจากนี้ คุณยังทำให้แคชการดาวน์โหลดพร้อมใช้งานสำหรับอินสแตนซ์ MediaSource
แต่ละรายการได้ ซึ่งสามารถส่งไปยังโปรแกรมเล่นโดยตรงโดยทำดังนี้
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();
การดาวน์โหลดและเล่นสตรีมแบบปรับอัตโนมัติ
โดยปกติแล้วสตรีมที่ปรับเปลี่ยนได้ (เช่น DASH, SmoothStreaming และ HLS) จะมีแทร็กสื่อหลายแทร็ก บ่อยครั้งที่มีแทร็กหลายรายการที่มีเนื้อหาเดียวกันในคุณภาพที่แตกต่างกัน (เช่น แทร็กวิดีโอ SD, HD และ 4K) นอกจากนี้ อาจมีแทร็กหลายรายการในประเภทเดียวกันซึ่งมีเนื้อหาแตกต่างกัน (เช่น แทร็กเสียงหลายรายการในภาษาต่างๆ)
สําหรับการเล่นสตรีมมิง คุณสามารถใช้ตัวเลือกแทร็กเพื่อเลือกแทร็กที่จะเล่น ในทำนองเดียวกัน ในการดาวน์โหลด คุณใช้ DownloadHelper
เพื่อเลือกแทร็กที่จะดาวน์โหลดได้ การใช้งาน DownloadHelper
แบบทั่วไปมีขั้นตอนดังนี้
- สร้าง
DownloadHelper
โดยใช้วิธีใดวิธีหนึ่งต่อไปนี้ของDownloadHelper.forMediaItem
โปรดเตรียมผู้ช่วยเหลือและรอให้ระบบติดต่อกลับ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);
- (ไม่บังคับ) ตรวจสอบแทร็กเริ่มต้นที่เลือกโดยใช้
getMappedTrackInfo
และgetTrackSelections
และทำการปรับโดยใช้clearTrackSelections
,replaceTrackSelections
และaddTrackSelection
- สร้าง
DownloadRequest
สำหรับแทร็กที่คุณเลือกโดยเรียกใช้getDownloadRequest
คุณสามารถส่งคำขอไปยังDownloadService
เพื่อเพิ่มการดาวน์โหลดตามที่อธิบายไว้ข้างต้น - ปล่อยตัวช่วยโดยใช้
release()
การเล่นเนื้อหาแบบปรับเปลี่ยนได้ซึ่งดาวน์โหลดไว้ต้องกำหนดค่าโปรแกรมเล่นและส่ง MediaItem
ที่เกี่ยวข้องตามที่อธิบายไว้ข้างต้น
เมื่อสร้าง MediaItem
คุณต้องตั้งค่า MediaItem.localConfiguration.streamKeys
ให้ตรงกับใน DownloadRequest
เพื่อให้โปรแกรมเล่นพยายามเล่นเฉพาะกลุ่มย่อยของแทร็กที่ดาวน์โหลดเท่านั้น การใช้ Download.request.toMediaItem
และ DownloadRequest.toMediaItem
เพื่อสร้าง MediaItem
จะจัดการเรื่องนี้ให้คุณ