AndroidX Media3 遷移指南

目前使用獨立 com.google.android.exoplayer2 程式庫和 androidx.media 的應用程式,應遷移至 androidx.media3。請使用遷移指令碼,將 Gradle 建構檔案、Java 和 Kotlin 來源檔案,以及 XML 版面配置檔案從 ExoPlayer 2.19.1 遷移至 AndroidX Media3 1.1.1

總覽

遷移前,請參閱下列各節,進一步瞭解新 API 的優點、要遷移的 API,以及應用程式專案應符合的必要條件。

遷移至 Jetpack Media3 的理由

  • 這是新的 ExoPlayer,而 com.google.android.exoplayer2 已停止服務。
  • 透過 MediaBrowser/MediaController 存取跨元件/程序的播放器 API
  • 使用 MediaSessionMediaController API 的擴充功能
  • 透過精細的存取權控管機制通告播放功能。
  • 移除 MediaSessionConnectorPlayerNotificationManager,即可簡化應用程式
  • 與 media-compat 用戶端 API 回溯相容 (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)

要遷移至 AndroidX Media3 的媒體 API

  • ExoPlayer 及其擴充功能
    這包含舊版 ExoPlayer 專案的所有模組 (已淘汰的媒體工作階段模組除外)。視 com.google.android.exoplayer2 中的套件而定,可以使用遷移指令碼遷移應用程式或模組。
  • MediaSessionConnector (視 androidx.media:media:1.4.3+androidx.media.* 套件而定)
    移除 MediaSessionConnector 並改用 androidx.media3.session.MediaSession
  • MediaBrowserServiceCompat (視 androidx.media:media:1.4.3+androidx.media.* 套件而定)
    androidx.media.MediaBrowserServiceCompat 的子類別遷移至 androidx.media3.session.MediaLibraryService,並將使用 MediaBrowserCompat.MediaItem 的程式碼遷移至 androidx.media3.common.MediaItem
  • MediaBrowserCompat (視 androidx.media:media:1.4.3+android.support.v4.media.* 套件而定)
    使用 MediaBrowserCompatMediaControllerCompat 遷移用戶端程式碼,以搭配 androidx.media3.common.MediaItem 使用 androidx.media3.session.MediaBrowser

必要條件

  1. 確認專案受到原始碼控管

    確認遷移工具套用指令碼後,您可以輕鬆還原變更。如果您尚未對專案進行原始碼控管,不妨立即開始使用。如果您因為某些原因而不想這麼做,請在開始遷移之前先為專案建立備份。

  2. 更新應用程式

    • 建議您將專案更新為使用最新版本的 ExoPlayer 程式庫,並移除對已淘汰方法的呼叫。如果打算使用指令碼執行遷移,則更新的版本必須與指令碼處理的版本相符。

    • 請將 compileSdkVersion 應用程式提高為至少 32

    • 將 Gradle 和 Android Studio Gradle 外掛程式升級為支援以上更新依附元件的最新版本。舉例來說:

      • Android Gradle 外掛程式版本:7.1.0
      • Gradle 版本:7.4
    • 替換使用 asterix (*) 並使用完整匯入陳述式的所有萬用字元匯入陳述式:刪除萬用字元匯入陳述式,然後使用 Android Studio 匯入完整陳述式 (F2 - Alt/Enter、F2 - Alt/Enter、...)。

    • com.google.android.exoplayer2.PlayerView 遷移至 com.google.android.exoplayer2.StyledPlayerView。此為必要操作,因為 AndroidX Media3 中沒有與 com.google.android.exoplayer2.PlayerView 對等的項目。

透過指令碼支援遷移 ExoPlayer

指令碼有助於從 com.google.android.exoplayer2 移至 androidx.media3 下的新套件和模組結構。這個指令碼會對專案套用一些驗證檢查。如果驗證失敗,就會顯示警告。否則,它會在以 Java 或 Kotlin 編寫的 Android Gradle 專案資源中,套用已重新命名的類別和套件的對應

usage: ./media3-migration.sh [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]
 PROJECT_ROOT: path to your project root (location of 'gradlew')
 -p: list package mappings and then exit
 -c: list class mappings (precedence over package mappings) and then exit
 -d: list dependency mappings and then exit
 -l: list files that will be considered for rewrite and then exit
 -x: exclude the path from the list of file to be changed: 'app/src/test'
 -m: migrate packages, classes and dependencies to AndroidX Media3
 -f: force the action even when validation fails
 -v: print the exoplayer2/media3 version strings of this script
 -h, --help: show this help text

使用遷移指令碼

  1. 從與已更新應用程式的版本相對應的 GitHub 上,從 ExoPlayer 專案的標記下載遷移指令碼

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. 將指令碼設為可執行狀態:

    chmod 744 media3-migration.sh
    
  3. 使用 --help 執行指令碼以瞭解各種選項。

  4. 使用 -l 執行指令碼,列出要遷移的一組檔案 (使用 -f 強制清單項目而不顯示警告訊息):

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. 使用 -m 執行指令碼,將套件、類別和模組對應至 Media3。使用 -m 選項執行指令碼會將變更套用至所選檔案。

    • 解決驗證錯誤而不進行變更
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • 強制執行

    如果指令碼判定違反先決條件,就可以使用 -f 標記強制執行遷移作業:

    ./media3-migration.sh -m -f /path/to/gradle/project/root
    
 # list files selected for migration when excluding paths
 ./media3-migration.sh -l -x "app/src/test/" -x "service/" /path/to/project/root
 # migrate the selected files
 ./media3-migration.sh -m -x "app/src/test/" -x "service/" /path/to/project/root

使用 -m 選項執行指令碼後,請完成以下手動步驟:

  1. 檢查指令碼如何更改程式碼:使用差異工具並修正潛在問題 (如果您認為指令碼在未傳遞 -f 選項的情況下發生一般問題,請考慮提交錯誤)。
  2. 建立專案:使用 ./gradlew clean build,或在 Android Studio 中依序選擇「File」>「Sync Project with Gradle Files」,然後依序選擇「Build」>「Clean project」和「Build」>「Rebuild project」 (在 Android Studio 的「Build - Build Output」分頁監控建構作業)。

建議後續追蹤步驟:

  1. 解決採用不穩定的 API 相關錯誤
  2. 取代已淘汰的 API 呼叫:使用建議的替代 API。將滑鼠指標懸停在 Android Studio 的警告上,然後參閱已淘汰符號的 JavaDoc,瞭解要使用哪些項目而非特定呼叫。
  3. 排序匯入陳述式:在 Android Studio 中開啟專案,然後在專案檢視器中的套件資料夾節點上按一下滑鼠右鍵,然後在包含變更來源檔案的套件上,選擇「Optimize imports」

androidx.media3.session.MediaSession 取代 MediaSessionConnector

在舊版 MediaSessionCompat 環境中,MediaSessionConnector 負責將玩家的狀態與工作階段狀態同步,並接收需要委派給適當的玩家方法的控制器指令。使用 AndroidX Media3 時,由 MediaSession 直接執行,不需要使用連接器。

  1. 移除所有 MediaSessionConnector 的參照與用法:如果您使用自動化指令碼遷移 ExoPlayer 類別和套件,指令碼可能使程式碼處於無法解決的 MediaSessionConnector 狀態無法遵循的狀態。當您嘗試建構或啟動應用程式時,Android Studio 會顯示毀損的程式碼。

  2. 在維護依附元件的 build.gradle 檔案中,新增AndroidX Media3 工作階段模組的實作依附元件,並移除舊版依附元件:

    implementation "androidx.media3:media3-session:1.3.1"
    
  3. MediaSessionCompat 替換為 androidx.media3.session.MediaSession

  4. 在建立舊版 MediaSessionCompat 的程式碼網站中,使用 androidx.media3.session.MediaSession.Builder 建構 MediaSession傳遞播放器以建構工作階段建立工具。

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player)
        .setSessionCallback(MySessionCallback())
        .build()
    
  5. 依據應用程式的要求實作 MySessionCallback。這是選用步驟。如要允許控制器將媒體項目新增至播放器,請實作 MediaSession.Callback.onAddMediaItems()。它提供多種目前和舊版 API 方法,可將媒體項目加入播放器,以回溯相容的方式播放。這包括 Media3 控制器的 MediaController.set/addMediaItems() 方法,以及舊版 API 的 TransportControls.prepareFrom*/playFrom* 方法。您可以在工作階段試用版應用程式的 PlaybackService 中找到 onAddMediaItems 的實作範例。

  6. 在遷移作業前刪除工作階段的程式碼網站上,釋出媒體工作階段:

    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    

Media3 中的 MediaSessionConnector 功能

下表顯示的 Media3 API 會處理先前在 MediaSessionConnector 中實作的功能。

MediaSessionConnectorAndroidX 媒體 3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setCustomLayout()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() 會在內部呼叫)
QueueNavigator ForwardingPlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

MediaBrowserService 遷移至 MediaLibraryService

AndroidX Media3 導入 MediaLibraryService 取代 MediaBrowserServiceCompatMediaLibraryService 的 JavaDoc 及其父類別 MediaSessionService 提供了有關 API 和服務的非同步程式設計模型的正確介紹。

MediaLibraryServiceMediaBrowserService 回溯相容。使用 MediaBrowserCompatMediaControllerCompat 的用戶端應用程式在連線至 MediaLibraryService 時,可在不變更程式碼的情況下繼續運作。對用戶端而言,無論應用程式是使用 MediaLibraryService 還是舊版 MediaBrowserServiceCompat,都是透明的。

含有服務、活動和外部應用程式的應用程式元件圖表。
圖 1:媒體應用程式元件總覽
  1. 為了維持回溯相容性,您必須在 AndroidManifest.xml 中透過服務註冊這兩個服務介面。如此一來,用戶端就能透過所需的服務介面找到您的服務:

    <service android:name=".MusicService" android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaLibraryService"/>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    
  2. 在維護依附元件的 build.gradle 檔案中,將實作依附元件新增至 AndroidX Media3 工作階段模組,並移除舊版依附元件:

    implementation "androidx.media3:media3-session:1.3.1"
    
  3. 將您的服務變更為沿用 MediaLibraryService (而非 MediaBrowserService)。如先前所述,MediaLibraryService 與舊版 MediaBrowserService 相容。因此,服務提供給用戶端的更廣泛 API 仍會維持不變。因此,應用程式可能可以保留實作 MediaBrowserService 所需的大部分邏輯,並根據新的 MediaLibraryService 進行調整。

    與舊版 MediaBrowserServiceCompat 的主要差異如下:

    • 實作服務生命週期方法:需要在服務本身上覆寫的方法 onCreate/onDestroy,其中應用程式會分配/釋出程式庫工作階段、播放器和其他資源。除了標準服務生命週期方法外,應用程式還需要覆寫 onGetSession(MediaSession.ControllerInfo),才能傳回在 onCreate 中建構的 MediaLibrarySession

    • 實作 MediaLibraryService.MediaLibrarySessionCallback:建構工作階段需要實作實際網域 API 方法的 MediaLibraryService.MediaLibrarySessionCallback。因此,您將覆寫 MediaLibrarySession.Callback 的方法,而不會覆寫舊版服務的 API 方法。

      接著,回呼會用於建構 MediaLibrarySession

      mediaLibrarySession =
            MediaLibrarySession.Builder(this, player, MySessionCallback())
               .build()
      

      請參閱 API 說明文件中的「MediaLibrarySessionCallback 完整 API」。

    • 實作 MediaSession.Callback.onAddMediaItems():回呼 onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>) 提供多種目前和舊版 API 方法,可將媒體項目新增至播放器,以回溯相容的方式播放。這包括 Media3 控制器的 MediaController.set/addMediaItems() 方法,以及舊版 API 的 TransportControls.prepareFrom*/playFrom* 方法。您可以在工作階段示範應用程式的 PlaybackService 中找到回呼的實作範例。

    • AndroidX Media3 使用 androidx.media3.common.MediaItem,而非 MediaBrowserCompat.MediaItemMediaMetadataCompat。與舊版類別繫結的程式碼部分需要據此變更,或改為對應至 Media3 MediaItem

    • 一般非同步程式設計模型變更為 Futures,相對於 MediaBrowserServiceCompat 的可卸離 Result 方法。您的服務實作項目可以傳回非同步 ListenableFuture,而非卸離結果,或傳回立即 Future 以便直接傳回值

移除 PlayerNotificationManager

MediaLibraryService 會自動支援媒體通知,且在使用 MediaLibraryServiceMediaSessionService 時可移除 PlayerNotificationManager

應用程式可以在 onCreate() 中設定自訂 MediaNotification.Provider 來取代 DefaultMediaNotificationProvider,藉此自訂通知。接著,MediaLibraryService 會視需要在前景啟動服務。

透過覆寫 MediaLibraryService.updateNotification(),應用程式可以進一步取得發布通知的完整擁有權,並視需要在前景啟動/停止服務。

使用 MediaBrowser 遷移用戶端程式碼

在 AndroidX Media3 中,MediaBrowser 會實作 MediaController/Player 介面,且除了瀏覽媒體程式庫之外,可用於控制媒體播放。如果必須在舊版環境中建立 MediaBrowserCompatMediaControllerCompat,可以只使用 Media3 中的 MediaBrowser 進行相同操作。

可以建構 MediaBrowser,並等待連線至正在建立的服務:

scope.launch {
    val sessionToken =
        SessionToken(context, ComponentName(context, MusicService::class.java)
    browser =
        MediaBrowser.Builder(context, sessionToken))
            .setListener(BrowserListener())
            .buildAsync()
            .await()
    // Get the library root to start browsing the library.
    root = browser.getLibraryRoot(/* params= */ null).await();
    // Add a MediaController.Listener to listen to player state events.
    browser.addListener(playerListener)
    playerView.setPlayer(browser)
}

請參閱「在媒體工作階段中控管播放功能」,瞭解如何建立在背景控製播放作業的 MediaController

後續步驟與清理

不穩定的 API 錯誤

遷移至 Media3 後,您可能會看到關於 API 使用不穩定的 Lint 錯誤。這些 API 可以安全使用,Lint 錯誤是我們新的二進位相容性保證的附加產品。如果您不需要嚴格的二進位檔相容性,可以使用 @OptIn 註解安全地隱藏這些錯誤。

背景

ExoPlayer v1 或 v2 都無法保證程式庫在後續版本之間的二進位檔相容性。ExoPlayer API 介面在設計上非常大,讓應用程式能自訂播放過程中幾乎所有層面的設定。後續版本的 ExoPlayer 有時會引入符號重新命名或其他破壞性變更 (例如介面中的新必要方法)。在大多數情況下,我們都會引入新符號來解決部分版本的舊符號,藉此減少這些破壞問題,讓開發人員有時間遷移使用情況,但這並非每次都會發生的情況。

這些破壞性變更造成 ExoPlayer v1 和 v2 程式庫的使用者有兩個問題:

  1. 升級至 ExoPlayer 版本可能會導致程式碼停止編譯。
  2. 如果應用程式直接和透過中繼程式庫依附於 ExoPlayer,就必須確保這兩個依附元件的版本相同,否則二進位檔不相容可能會導致執行階段異常終止。

Media3 中的改善項目

Media3 保證部分 API 介面的二進位檔相容性。「無法」保證二進位檔相容性的部分會標上 @UnstableApi。為了清楚區分,使用不穩定的 API 符號會產生 Lint 錯誤,除非這些符號加上 @OptIn 註解。

從 ExoPlayer v2 遷移至 Media3 後,您會看到許多不穩定的 API Lint 錯誤。因此,Media3 的穩定性似乎比 ExoPlayer 第 2 版低。但此聲明與事實不符。Media3 API 的「不穩定」部分與 ExoPlayer v2 API 介面的「整個」等級相同,且 ExoPlayer v2 不提供穩定 Media3 API 介面的保證。差別在於,現在 Lint 錯誤會提醒您不同層級的穩定性。

處理不穩定的 API Lint 錯誤

請參閱這類 Lint 錯誤的疑難排解章節,進一步瞭解如何使用 @OptIn 為不穩定 API 的 Java 和 Kotlin 使用情形加上註解。

已淘汰的 API

您可能會注意到在 Android Studio 中,呼叫已淘汰 API 的呼叫會加上刪除線。建議您以適當的替代方案取代這類呼叫。將滑鼠遊標懸停在符號上,即可查看已改用哪個 API 的 JavaDoc。

螢幕截圖:如何以已淘汰的方法顯示 JavaDoc
圖 3:Android Studio 中的 JavaDoc 工具提示,建議您可替代任何已淘汰的符號。

程式碼範例和試用版應用程式