Jetpack Media3 定義了 Player 介面,其中列出播放影片和音訊檔案的基本功能。ExoPlayer 是 Media3 中這個介面的預設實作。建議使用 ExoPlayer,因為它提供完整的功能組合,涵蓋大多數的播放用途,而且可自訂,以處理您可能有的任何其他用途。此外,ExoPlayer 也會將裝置和作業系統的片段抽離,讓您的程式碼在整個 Android 生態系統中都能一致運作。ExoPlayer 包含:
- 支援播放清單
- 支援多種漸進式和自適應串流格式
- 支援用戶端和伺服器端廣告插播
- 支援受 DRM 保護的內容播放
本頁將逐步說明建構播放應用程式的一些重要步驟,如需更多詳細資料,請參閱「Media3 ExoPlayer」完整指南。
開始使用
如要開始使用,請新增 Jetpack Media3 的 ExoPlayer、UI 和 Common 模組依附元件:
implementation "androidx.media3:media3-exoplayer:1.9.2" implementation "androidx.media3:media3-ui:1.9.2" implementation "androidx.media3:media3-common:1.9.2"
視用途而定,您可能也需要 Media3 的其他模組,例如 exoplayer-dash,才能播放 DASH 格式的串流。
請務必將 1.9.2 替換為偏好的程式庫版本。如要查看最新版本,請參閱「版本資訊」。
建立媒體播放器
使用 Media3 時,您可以選擇使用隨附的 Player 介面實作項目 ExoPlayer,也可以自行建構自訂實作項目。
建立 ExoPlayer
建立 ExoPlayer 執行個體最簡單的方法如下:
Kotlin
val player = ExoPlayer.Builder(context).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build();
您可以在 Activity、Fragment 或 Service 的 onCreate() 生命週期方法中建立媒體播放器。
Builder 包含一系列自訂選項,您可能會感興趣,例如:
setAudioAttributes()設定音訊焦點處理方式setHandleAudioBecomingNoisy()設定音訊輸出裝置中斷連線時的播放行為setTrackSelector()設定測試群組選取
Media3 提供 PlayerView UI 元件,可加入應用程式的版面配置檔案。這個元件會封裝 PlayerControlView (用於播放控制項)、SubtitleView (用於顯示字幕) 和 Surface (用於算繪影片)。
準備播放器
使用 setMediaItem() 和 addMediaItem() 等方法,將媒體項目新增至播放清單,以便播放。然後呼叫 prepare(),開始載入媒體並取得必要資源。
應用程式處於前景時,您才應執行這些步驟。如果播放器處於 Activity 或 Fragment 狀態,表示您在 API 級別 24 以上版本中,於 onStart() 生命週期方法準備播放器,或在 API 級別 23 以下版本中,於 onResume() 生命週期方法準備播放器。如要為 Service 中的球員準備,請前往 onCreate()。如要瞭解如何實作生命週期方法,請參閱 ExoPlayer 程式碼研究室。
控制播放器
準備好播放器後,您可以在播放器上呼叫方法來控制播放作業,例如:
play()和pause()開始及暫停播放seekTo()即可跳轉至目前媒體項目的特定位置seekToNextMediaItem()和seekToPreviousMediaItem()即可瀏覽播放清單
繫結至播放器時,PlayerView 或 PlayerControlView 等 UI 元件會相應更新。
釋放播放器
播放影片可能需要資源 (例如影片解碼器),因此不再需要播放器時,請務必呼叫 release(),釋出資源。
如果播放器位於 Activity 或 Fragment 中,請在 API 級別 24 以上版本的 onStop() 生命週期方法中,或在 API 級別 23 以下版本的 onPause() 方法中,釋放播放器。如要釋放 Service 中的球員,請前往 onDestroy()。如要瞭解如何實作生命週期方法,請參閱 ExoPlayer 程式碼研究室。
使用媒體工作階段管理播放
在 Android 上,媒體工作階段提供標準化方式,可跨程序界線與媒體播放器互動。將媒體工作階段連結至播放器,即可在外部宣傳媒體播放作業,並接收來自外部來源的播放指令,例如整合行動裝置和大螢幕裝置上的系統媒體控制項。
如要使用媒體工作階段,請新增 Media3 Session 模組的依附元件:
implementation "androidx.media3:media3-session:1.9.2"
建立媒體工作階段
初始化播放器後,您可以按照下列方式建立 MediaSession:
Kotlin
val player = ExoPlayer.Builder(context).build() val mediaSession = MediaSession.Builder(context, player).build()
Java
ExoPlayer player = new ExoPlayer.Builder(context).build(); MediaSession mediaSession = new MediaSession.Builder(context, player).build();
Media3 會自動將 Player 的狀態與 MediaSession 的狀態同步。這項功能適用於任何 Player 實作項目,包括 ExoPlayer、CastPlayer 或自訂實作項目。
將控制權授予其他用戶端
用戶端應用程式可以實作媒體控制器,控制媒體工作階段的播放作業。如要接收這些要求,請在建構 MediaSession 時設定 callback 物件。
當控制器即將連線至媒體工作階段時,系統會呼叫 onConnect() 方法。您可以根據提供的 ControllerInfo 決定是否要接受或拒絕要求。如需相關範例,請參閱 Media3 Session 示範應用程式。
連線後,遙控器就能將播放指令傳送至工作階段。然後,工作階段會將這些指令委派給播放器。工作階段會自動處理 Player 介面中定義的播放和播放清單指令。
其他回呼方法可讓您處理要求,例如自訂播放指令和修改播放清單。這些回呼同樣包含 ControllerInfo 物件,因此您可以根據要求逐一判斷存取控管機制。
在背景播放媒體
如要在應用程式不在前景時繼續播放媒體 (例如在使用者未開啟應用程式時播放音樂、有聲書或 Podcast),請將 Player 和 MediaSession 封裝在前景服務中。為此,Media3 提供 MediaSessionService 介面。
實作 MediaSessionService
建立擴充 MediaSessionService 的類別,並在 onCreate() 生命週期方法中例項化 MediaSession。
Kotlin
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your Player and MediaSession in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
Java
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
在資訊清單中,新增 Service 類別和 MediaSessionService 意圖篩選器,並要求 FOREGROUND_SERVICE 權限來執行前景服務:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
最後,在您建立的類別中,覆寫 onGetSession() 方法,控管用戶端對媒體工作階段的存取權。傳回 MediaSession 即可接受連線要求,傳回 null 則可拒絕要求。
Kotlin
// This example always accepts the connection request override fun onGetSession( controllerInfo: MediaSession.ControllerInfo ): MediaSession? = mediaSession
Java
@Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { // This example always accepts the connection request return mediaSession; }
連結至使用者介面
現在媒體工作階段與播放器 UI 所在的 Activity 或 Fragment 分開,因此您可以使用 MediaController 將兩者連結在一起。Service在 Activity 或 Fragment 的 onStart() 方法中,使用您的 UI 建立 MediaSession 的 SessionToken,然後使用 SessionToken 建構 MediaController。建構 MediaController 是非同步作業。
Kotlin
override fun onStart() { val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java)) val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync() controllerFuture.addListener( { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()) }, MoreExecutors.directExecutor() ) }
Java
@Override public void onStart() { SessionToken sessionToken = new SessionToken(this, new ComponentName(this, PlaybackService.class)); ListenableFuture<MediaController> controllerFuture = new MediaController.Builder(this, sessionToken).buildAsync(); controllerFuture.addListener(() -> { // Call controllerFuture.get() to retrieve the MediaController. // MediaController implements the Player interface, so it can be // attached to the PlayerView UI component. playerView.setPlayer(controllerFuture.get()); }, MoreExecutors.directExecutor()) }
MediaController 會實作 Player 介面,因此您可以使用 play() 和 pause() 等相同方法來控制播放作業。與其他元件類似,請記得在不再需要 MediaController 時釋出,例如呼叫 MediaController.releaseFuture(),釋出 Activity 的 onStop() 生命週期方法。
發布通知
前景服務必須在啟動時發布通知。A
MediaSessionService 會自動為您建立 MediaStyle 通知,並以 MediaNotification 形式顯示。
如要提供自訂通知,請建立 MediaNotification.Provider,並使用 DefaultMediaNotificationProvider.Builder,或是建立提供者介面的自訂實作項目。使用 setMediaNotificationProvider 將提供者新增至 MediaSession。
宣傳內容庫
MediaLibraryService 是以 MediaSessionService 為基礎,可讓用戶端應用程式瀏覽應用程式提供的媒體內容。用戶端應用程式會實作 MediaBrowser,與 MediaLibraryService 互動。
實作 MediaLibraryService 的方式與實作 MediaSessionService 類似,但您應在 onGetSession() 中傳回 MediaLibrarySession,而非 MediaSession。相較於 MediaSession.Callback,MediaLibrarySession.Callback 包含其他方法,可讓瀏覽器用戶端瀏覽程式庫服務提供的內容。
與 MediaSessionService 類似,在資訊清單中宣告 MediaLibraryService,並要求 FOREGROUND_SERVICE 權限來執行前景服務:
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaLibraryService"/>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
上述範例包含 MediaLibraryService 的意圖篩選器,以及為了回溯相容性而加入的舊版 MediaBrowserService。額外意圖篩選器可讓使用 MediaBrowserCompat API 的用戶端應用程式辨識 Service。
MediaLibrarySession 可讓您以樹狀結構提供內容資料庫,並使用單一根 MediaItem。樹狀結構中的每個 MediaItem 都可以有任意數量的子項 MediaItem 節點。您可以根據用戶端應用程式的要求,提供不同的根或樹狀結構。舉例來說,您傳回給尋找建議媒體項目清單的用戶端的樹狀結構,可能只包含根 MediaItem 和單一層級的子項 MediaItem 節點,而您傳回給其他用戶端應用程式的樹狀結構,則可能代表更完整的內容庫。
建立 MediaLibrarySession
MediaLibrarySession 會擴充 MediaSession API,加入內容瀏覽 API。相較於 MediaSession 回呼,MediaLibrarySession 回呼新增了下列方法:
onGetLibraryRoot()當用戶端要求內容樹狀結構的根MediaItem時onGetChildren()for when a client requests the children of aMediaItemin the content treeonGetSearchResult()當用戶端要求從內容樹狀結構中取得特定查詢的搜尋結果時
相關回呼方法會包含 LibraryParams 物件,其中含有用戶端應用程式感興趣的內容樹狀結構類型相關額外信號。