在以媒體為中心的應用程式中,提供流暢無間斷的播放體驗,是打造優質使用者體驗的關鍵。使用者希望影片能立即開始播放,且過程流暢無間斷。
核心挑戰是延遲。傳統上,使用者選擇要播放的項目後,影片播放器才會開始運作,包括連線、下載、剖析及緩衝處理。在當今短片盛行的環境下,這種被動做法速度太慢。解決方法是主動出擊。我們需要預測使用者接下來會觀看的內容,並預先準備好。這就是預先載入的本質。
預先載入的主要優點包括:
- 🚀 更快開始播放:影片已準備就緒,因此項目之間的轉場效果更快,且能立即開始播放。
- 📉 減少緩衝:主動載入資料可大幅減少播放停頓的機率,例如因網路不穩而停頓。
- ✨ 提升使用者體驗:啟動速度更快,緩衝時間更短,使用者互動更流暢。
在這個三部曲系列中,我們將介紹並深入探討 Media3 強大的公用程式,瞭解如何 (預先) 載入元件。
- 在第 1 部分中,我們將介紹基礎知識:瞭解 Media3 中提供的不同預先載入策略、啟用 PreloadConfiguration 和設定 DefaultPreloadManager,以及讓應用程式預先載入項目。看完這篇網誌後,您應該就能預先載入及播放媒體項目,並設定排名和時間長度。
- 在第 2 部分中,我們將深入探討 DefaultPreloadManager 的進階主題:使用監聽器進行數據分析,以及探索適用於可部署於正式環境的最佳做法,例如滑動視窗模式,以及 DefaultPreloadManager 和 ExoPlayer 的自訂共用元件。
- 在第 3 部分,我們將深入探討如何使用 DefaultPreloadManager 進行磁碟快取。
預先載入功能可派上用場!🦸♀️
預先載入背後的概念很簡單:在需要媒體內容之前先載入。使用者滑動到下一部影片時,系統已下載並提供影片的前幾段內容,可立即播放。
這就像餐廳一樣,忙碌的廚房不會等到訂單來了才開始切洋蔥。🧅 他們會預先做好準備工作。預先載入是影片播放器的準備工作。
啟用預先載入功能後,如果使用者在播放緩衝區到達下一個項目之前跳至下一個項目,預先載入功能有助於縮短加入延遲時間。系統會準備下一個時段的第一個週期,並緩衝處理影片、音訊和文字樣本。預先載入的期間稍後會排入播放器佇列,緩衝的樣本會立即提供,並準備好饋送至轉碼器進行算繪。
Media3 提供兩種主要預先載入 API,分別適用於不同用途。選擇合適的 API 是第一步。
1. 使用 PreloadConfiguration 預先載入播放清單項目
這是簡單的做法,適用於播放順序可預測的線性連續媒體,例如播放清單 (如一系列劇集)。您可以使用 ExoPlayer 的播放清單 API,將完整媒體項目清單提供給播放器,並為播放器設定 PreloadConfiguration,然後播放器就會自動預先載入序列中的下一個項目 (如設定)。如果使用者在播放緩衝區已重疊到下一個項目之前跳至下一個項目,這項 API 會嘗試縮短加入延遲時間。
只有在目前播放作業未載入任何媒體時,系統才會開始預先載入,避免與主要播放作業爭用頻寬。
如果您仍不確定是否需要預先載入,這個 API 是絕佳的低負擔選項,可供您試用!
player.preloadConfiguration =
PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)
使用上述 PreloadConfiguration,播放器會嘗試預先載入播放清單中下一個項目五秒的媒體。
加入後,如要再次關閉播放清單預先載入功能,請使用 PreloadConfiguration.DEFAULT 停用播放清單預先載入功能:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. 使用 PreloadManager 預先載入動態清單
如果是直向動態饋給或輪播等動態 UI,其中「下一個」項目是由使用者互動決定,則適合使用 PreloadManager API。這是 Media3 ExoPlayer 程式庫中功能強大的獨立元件,專為主動預先載入而設計。這項工具會管理一系列可能的 MediaSource,並根據與使用者目前位置的距離設定優先順序,還能精細控管要預先載入的內容,適用於動態短片動態饋給等複雜情境。
設定 PreloadManager
DefaultPreloadManager 是 PreloadManager 的標準實作。
DefaultPreloadManager 的建構工具可以建構 DefaultPreloadManager 和任何 ExoPlayer 執行個體,這些執行個體會播放預先載入的內容。如要建立 DefaultPreloadManager,您需要傳遞 TargetPreloadStatusControl,預先載入管理工具可查詢這項控制項,瞭解要為項目載入多少內容。我們將在下節說明並定義 TargetPreloadStatusControl 的範例。
val preloadManagerBuilder = DefaultPreloadManager.Builder(context, targetPreloadStatusControl) val preloadManager = val preloadManagerBuilder.build() // Build ExoPlayer with DefaultPreloadManager.Builder val player = preloadManagerBuilder.buildExoPlayer()
ExoPlayer 和 DefaultPreloadManager 必須使用相同的 builder,確保兩者底層的元件正確共用。
就是這麼簡單,現在管理員已準備好接收指示。
使用 TargetPreloadStatusControl 設定時間長度和排名
如果想預先載入 10 秒的影片,您可以提供輪播介面中媒體項目的位置,DefaultPreloadManager 會根據項目與使用者目前播放項目的距離,優先載入項目。
如要控制預先載入項目的時間長度,可以透過 DefaultPreloadManager.PreloadStatus 回傳。
例如:
- 項目「A」的優先順序最高,載入 5 秒的影片。
- 項目「B」的優先順序為中等,但當您處理到這個項目時,請載入 3 秒的影片。
- 項目「C」的優先順序較低,只追蹤載入。
- 項目「D」的優先順序更低,只要準備即可。
- 其他項目距離很遠,請勿預先載入任何內容。
這種精細控管機制有助於提高資源使用率,建議您採用此做法,確保影片順暢播放。
import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus
class MyTargetPreloadStatusControl(
currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {
// The app is responsible for updating this based on UI state
override fun getTargetPreloadStatus(index: Int): PreloadStatus? {
val distance = index - currentPlayingIndex
// Adjacent items (Next): preload 5 seconds
if (distance == 1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(5000L)
}
// Adjacent items (Previous): preload 3 seconds
else if (distance == -1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(3000L)
}
// Items two positions away: just select tracks
else if (distance) == 2) {
// Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
return PreloadStatus.TRACKS_SELECTED
}
// Items four positions away: just select prepare
else if (abs(distance) <= 4) {
// Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
return PreloadStatus.SOURCE_PREPARED
}
// All other items are too far away
return null
}
}
提示:PreloadManager 可以預先載入前一個和下一個項目,但 PreloadConfiguration 只會預先載入下一個項目。
管理預先載入項目
建立管理員後,即可開始告知管理員要處理的工作。當使用者捲動瀏覽動態消息時,您會找出即將播放的影片,並將這些影片加入管理員。與 PreloadManager 的互動是 UI 和預先載入引擎之間以狀態為導向的對話。
1. 新增媒體項目
填入動態饋給時,請務必告知管理員需要追蹤的媒體。如果您是新手,可以加入要預先載入的完整清單。之後可以視需要繼續在清單中新增單一項目。您可以完全掌控預先載入清單中的項目,也就是說,您也必須管理管理工具中新增和移除的項目。
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}
管理員現在會在背景開始擷取這項 MediaItem 的資料。
新增後,請告知管理員重新評估新清單 (提示有變更,例如新增/ 移除項目,或使用者改為播放新項目)。
preloadManager.invalidate()
2. 擷取及播放項目
接下來是主要的播放邏輯。使用者決定播放該影片時,您不需要建立新的 MediaSource。而是要求 PreloadManager 提供已準備好的內容。您可以使用 MediaItem 從 Preload Manager 擷取 MediaSource。
如果從 PreloadManager 擷取的項目為空值,表示 mediaItem 尚未預先載入或新增至 PreloadManager,因此您可以選擇直接設定 mediaItem。
// When a media item is about to display on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
player.setMediaSource(mediaSource)
} else {
// If mediaSource is null, that mediaItem hasn't been added yet.
// So, send it directly to the player.
player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()
準備從 PreloadManager 擷取的 MediaSource,即可使用記憶體中已有的資料,從預先載入無縫轉換至播放。這就是啟動時間縮短的原因。
3. 讓目前的索引與 UI 保持同步
由於動態消息 / 清單可能會變動,請務必將目前的播放索引通知 PreloadManager,這樣系統才能一律優先預先載入最接近目前索引的項目。
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. 移除項目
為確保管理員效率,您應移除不再需要追蹤的項目,例如遠離使用者目前位置的項目。
// When an item is too far from the current playing index preloadManager.remove(mediaItem)
如要一次清除所有項目,可以呼叫 preloadManager.reset()。
5. 釋放管理員
不再需要 PreloadManager 時 (例如 UI 遭到毀損時),請務必釋出該管理工具,以釋放資源。建議您在發布 Player 資源的位置執行這項操作。建議先釋出管理員,再釋出播放器,因為如果不需要預先載入任何內容,播放器可以繼續播放。
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
示範時間
立即查看實際運作情況 👍
在下方的示範中,我們可以看到 PreloadManager 的影響:右側的載入速度較快,左側則顯示現有的體驗。您也可以查看示範的程式碼範例。(額外功能:還會顯示每個影片的啟動延遲時間)
後續步驟
第 1 部分到此結束!您現在已具備建構動態預先載入系統的工具。您可以使用 PreloadConfiguration 在 ExoPlayer 中預先載入播放清單的下一個項目,也可以設定 DefaultPreloadManager、即時新增及移除項目、設定目標預先載入狀態,並正確擷取預先載入的內容以供播放。
在第 2 部分,我們將深入探討 DefaultPreloadManager。我們將探討如何監聽預先載入事件、討論最佳做法 (例如使用滑動視窗來避免記憶體問題),並深入瞭解 ExoPlayer 和 DefaultPreloadManager 的自訂共用元件。
有任何意見想分享嗎?期待收到您的回覆。
敬請期待,並開始加快應用程式速度!🚀
繼續閱讀
-
產品新訊
歡迎收看媒體預先載入系列影片的第二部,瞭解如何使用 Media3 預先載入媒體。本系列文章旨在引導您在 Android 應用程式中,建構高回應速度、低延遲的媒體體驗。
Mayuri Khinvasara Khabya • 閱讀時間:9 分鐘
-
產品新訊
盡可能確保 Google Play 提供最安全可靠的服務體驗。今天,我們宣布推出一系列新政策和帳戶轉移功能,進一步保障使用者隱私,並防範詐欺行為。
Bennet Manuel • 3 分鐘可讀完
-
產品新訊
現在使用 Android Emulator,就能輕鬆測試支援多種裝置的互動。
Steven Jenkins • 閱讀時間:2 分鐘
隨時掌握最新消息
每週透過電子郵件接收最新的 Android 開發洞察資料。