ExoPlayer มีฟังก์ชันการทำงานในการดาวน์โหลดสื่อเพื่อเล่นแบบออฟไลน์ ในกรณีการใช้งานส่วนใหญ่ คุณอาจต้องการให้การดาวน์โหลดดำเนินต่อไปแม้ว่าแอปจะทำงานในเบื้องหลังก็ตาม สำหรับกรณีการใช้งานเหล่านี้ แอปของคุณควรสร้างคลาสย่อยของ DownloadService และ
ส่งคำสั่งไปยังบริการเพื่อเพิ่ม นำออก และควบคุมการดาวน์โหลด
แผนภาพต่อไปนี้แสดงคลาสหลักๆ ที่เกี่ยวข้อง
DownloadService: ห่อหุ้มDownloadManagerและส่งต่อคำสั่งไปยังDownloadManagerบริการนี้ช่วยให้DownloadManagerทำงานต่อไปได้แม้ว่าแอปจะทำงานใน เบื้องหลังก็ตามDownloadManager: จัดการการดาวน์โหลดหลายรายการ โหลด (และจัดเก็บ) สถานะจาก (และไปยัง)DownloadIndexเริ่มและหยุดการดาวน์โหลดตามข้อกำหนดต่างๆ เช่น การเชื่อมต่อเครือข่าย เป็นต้น โดยปกติแล้ว หากต้องการดาวน์โหลดเนื้อหา ผู้จัดการจะอ่านข้อมูลที่ดาวน์โหลดจากHttpDataSourceและเขียนลงในCacheDownloadIndex: คงสถานะของการดาวน์โหลด
การสร้าง 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 ได้ แต่แอปจะใช้รูปแบบรหัสใดก็ได้ที่เหมาะกับ Use Case ของตนมากที่สุด 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() จากนั้นคุณจะได้รับเคอร์เซอร์ที่
วนซ้ำการดาวน์โหลดทั้งหมดโดยการเรียกใช้
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.Factoryเตรียม ผู้ช่วยและรอการโทรกลับ - ไม่บังคับ ตรวจสอบแทร็กที่เลือกเริ่มต้นโดยใช้
getMappedTrackInfoและgetTrackSelectionsแล้วปรับโดยใช้clearTrackSelectionsreplaceTrackSelectionsและaddTrackSelection - สร้าง
DownloadRequestสำหรับแทร็กที่เลือกโดยเรียกใช้getDownloadRequestคุณสามารถส่งคำขอไปยังDownloadServiceเพื่อ เพิ่มการดาวน์โหลดตามที่อธิบายไว้ข้างต้น - ปล่อยตัวช่วยโดยใช้
release()
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);
การเล่นเนื้อหาแบบปรับอัตโนมัติที่ดาวน์โหลดมาจะต้องกำหนดค่าเพลเยอร์และ
ส่ง MediaItem ที่เกี่ยวข้องตามที่อธิบายไว้ข้างต้น
เมื่อสร้าง MediaItem MediaItem.localConfiguration.streamKeys ต้องตั้งค่าให้ตรงกับค่าใน DownloadRequest เพื่อให้เพลเยอร์พยายามเล่นเฉพาะชุดย่อยของแทร็กที่ดาวน์โหลดมา การใช้
Download.request.toMediaItem และ DownloadRequest.toMediaItem เพื่อสร้าง
MediaItem จะช่วยจัดการเรื่องนี้ให้คุณ