在現今以媒體為主的應用程式中,提供流暢不中斷的播放體驗,是打造優質使用者體驗的關鍵。使用者希望影片能立即開始播放,且過程流暢不中斷。
核心挑戰是延遲。傳統上,影片播放器只有在使用者選擇要播放的項目後,才會開始運作 (連線、下載、剖析、緩衝)。這種被動做法在現今的短篇影片環境中,速度太慢。解決方法是主動出擊。我們需要預測使用者接下來會觀看的內容,並提前準備好。這就是預先載入的精髓。
預先載入的主要優點包括:
- 🚀 播放速度更快:影片已準備就緒,切換項目時更快速,播放更即時。
- 📉 減少緩衝:主動載入資料後,播放作業就不太可能因網路不穩等原因而中斷。
- ✨ 打造更流暢的使用者體驗:結合更快的啟動速度和更少的緩衝時間,為使用者提供更流暢、更無縫的互動體驗。
在這個三部曲系列中,我們將介紹並深入探討 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 必須使用相同的建構工具,確保兩者底層的元件能正確共用。
就是這麼簡單,現在,您已準備好接收指令的管理員。
使用 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,從預先載入管理工具擷取 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 分鐘 -
產品最新消息今天,我們在 Android 開發人員高峰會上宣布,將廣泛提供多項工具和 API,旨在消除日常摩擦,讓您更靈活地建構出色的 Android 應用程式。
Matthew McCullough • 閱讀時間:4 分鐘 -
產品最新消息Android Studio Rabbit 2 穩定版已推出,可供您在正式環境中使用。此版本包含多項重要功能,可提升開發效率,例如「自備代理」、全新 XR 模擬器功能、LeakCanary 更新、全新代理技能管理工具、AI 程式碼審查等。
Matthew Warner • 閱讀時間:3 分鐘
每週透過電子郵件接收 Android 開發人員的最新深入分析。