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 を使用して、コンポーネント/プロセス間で Player API にアクセスします。
  • MediaSession API と MediaController API の拡張機能を使用する。
  • きめ細かなアクセス制御により再生機能をアドバタイズする。
  • MediaSessionConnectorPlayerNotificationManager を削除してアプリを簡素化します。
  • media-compat クライアント API との下位互換性MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat

AndroidX Media3 に移行する Media API

  • ExoPlayer とその拡張機能
    これには、廃止された mediasession モジュールを除く、以前の ExoPlayer プロジェクトのすべてのモジュールが含まれます。com.google.android.exoplayer2 のパッケージに依存するアプリまたはモジュールは、移行スクリプトを使用して移行できます。
  • MediaSessionConnectorandroidx.media:media:1.4.3+androidx.media.* パッケージによって異なる)
    MediaSessionConnector を削除し、代わりに androidx.media3.session.MediaSession を使用します。
  • MediaBrowserServiceCompatandroidx.media:media:1.4.3+androidx.media.* パッケージに依存)
    androidx.media.MediaBrowserServiceCompat のサブクラスを androidx.media3.session.MediaLibraryService に移行し、MediaBrowserCompat.MediaItem を使用するコードを androidx.media3.common.MediaItem に移行します。
  • MediaBrowserCompatandroidx.media:media:1.4.3+android.support.v4.media.* パッケージに依存)
    MediaBrowserCompat または MediaControllerCompat を使用してクライアント コードを移行し、androidx.media3.common.MediaItemandroidx.media3.session.MediaBrowser を使用します。

前提条件

  1. プロジェクトがソース管理の対象であることを確認する

    スクリプトによる移行ツールで適用した変更を簡単に元に戻すことができることを確認します。プロジェクトがまだソース管理の対象になっていない場合は、今から始めることをおすすめします。なんらかの理由で移行しない場合は、移行を開始する前に、プロジェクトのバックアップ コピーを作成してください。

  2. アプリを更新する

    • ExoPlayer ライブラリの最新バージョンを使用するようにプロジェクトを更新し、非推奨のメソッドの呼び出しをすべて削除することをおすすめします。スクリプトを使用して移行する場合は、更新先のバージョンとスクリプトで処理されるバージョンを一致させる必要があります。

    • アプリの compileSdkVersion を 32 以上に引き上げる

    • 上記で更新した依存関係で動作する最新バージョンに、Gradle と Android Studio の Gradle プラグインをアップグレードします。次に例を示します。

      • Android Gradle プラグイン バージョン: 7.1.0
      • Gradle バージョン: 7.4
    • アスタリスク(*)を使用しているすべてのワイルドカード インポート ステートメントを置き換えます。ワイルドカード インポート ステートメントを削除し、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. アプリをアップデートしたバージョンに対応する ExoPlayer プロジェクトのタグから、移行スクリプトを GitHub でダウンロードします。

    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] を選択します。

MediaSessionConnectorandroidx.media3.session.MediaSession に置き換えます。

従来の MediaSessionCompat では、MediaSessionConnector は、プレーヤーの状態をセッションの状態と同期し、適切なプレーヤー メソッドへの委任が必要なコントローラからコマンドを受信する役割を担っていました。AndroidX Media3 では、コネクタを必要とせずに、MediaSession によって直接行われます。

  1. MediaSessionConnector の参照と使用をすべて削除する: 自動スクリプトを使用して ExoPlayer のクラスとパッケージを移行した場合、スクリプトが、解決できない MediaSessionConnector に関するコードをコンパイルできない状態のままにしている可能性があります。アプリをビルドまたは起動しようとすると、Android Studio に破損したコードが表示されます。

  2. 依存関係を維持する build.gradle ファイルで、AndroidX Media3 セッション モジュールに実装依存関係を追加し、従来の依存関係を削除します。

    implementation "androidx.media3:media3-session:1.2.1"
    
  3. MediaSessionCompatandroidx.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* メソッドが含まれます。onAddMediaItems の実装例については、セッション デモアプリの PlaybackService をご覧ください。

  6. 移行前にセッションを破棄したコードサイトでメディア セッションを解放します。

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

Media3 の MediaSessionConnector 機能

次の表に、以前に MediaSessionConnector に実装された機能を処理する Media3 API を示します。

MediaSessionConnector の使用AndroidX メディア 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

MediaBrowserServiceMediaLibraryService に移行する

AndroidX Media3 には、MediaBrowserServiceCompat に代わる MediaLibraryService が導入されています。MediaLibraryService の JavaDoc とそのスーパークラス MediaSessionService は、サービスの API および非同期プログラミング モデルについてわかりやすく説明する資料です。

MediaLibraryService には MediaBrowserService と下位互換性があります。MediaBrowserCompat または MediaControllerCompat を使用しているクライアント アプリは、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.2.1"
    
  3. MediaBrowserService ではなく MediaLibraryService を継承するようにサービスを変更します。前述のように、MediaLibraryService は従来の MediaBrowserService と互換性があります。したがって、サービスがクライアントに提供するより広範な API に変更はありません。そのため、MediaBrowserService の実装に必要なロジックのほとんどを保持し、新しい MediaLibraryService に適応できる可能性が高くなります。

    以前の MediaBrowserServiceCompat との主な違いは次のとおりです。

    • サービス ライフサイクル メソッドを実装する: サービス自体でオーバーライドする必要がある onCreate/onDestroy のメソッドは、アプリがライブラリ セッション、プレーヤー、その他のリソースの割り当てと解放を行います。標準のサービス ライフサイクル メソッドに加えて、アプリは onGetSession(MediaSession.ControllerInfo) をオーバーライドして、onCreate で構築された MediaLibrarySession を返す必要があります。

    • MediaLibraryService.MediaLibrarySessionCallback を実装する: セッションの構築には、実際のドメイン API メソッドを実装する MediaLibraryService.MediaLibrarySessionCallback が必要です。したがって、以前のサービスの API メソッドをオーバーライドする代わりに、MediaLibrarySession.Callback のメソッドをオーバーライドします。

      その後、このコールバックを使用して 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 は、MediaBrowserCompat.MediaItemMediaMetadataCompat の代わりに androidx.media3.common.MediaItem を使用しています。以前のクラスに関連付けられているコードの一部は、それに応じて変更するか、Media3 MediaItem にマッピングする必要があります。

    • MediaBrowserServiceCompat の取り外し可能な Result アプローチとは対照的に、一般的な非同期プログラミング モデルが Futures に変更されました。サービスの実装では、結果をデタッチする代わりに非同期の ListenableFuture を返すか、即時の Future を返して値を直接返すことができます。

PlayerNotificationManager を削除する

MediaLibraryService は自動的にメディア通知をサポートするため、MediaLibraryService または MediaSessionService の使用時に PlayerNotificationManager を削除できます。

DefaultMediaNotificationProvider を置き換えるカスタム MediaNotification.ProvideronCreate() で設定することで、アプリで通知をカスタマイズできます。その後、MediaLibraryService は必要に応じてフォアグラウンドでサービスを開始します。

MediaLibraryService.updateNotification() をオーバーライドすることで、アプリは、必要に応じて、通知の送信や、フォアグラウンドでのサービスの開始/停止に対する完全な所有権を持つことができます。

MediaBrowser を使用してクライアント コードを移行する

AndroidX Media3 では、MediaBrowserMediaController/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 ライブラリのユーザーに 2 つの問題が発生しました。

  1. ExoPlayer バージョンにアップグレードすると、コードのコンパイルが停止することがあります。
  2. 直接と中間ライブラリを介して ExoPlayer に依存するアプリでは、両方の依存関係が同じバージョンであることを確認する必要があります。そうしないと、バイナリの非互換性によってランタイムがクラッシュする可能性があります。

Media3 の改善点

Media3 は、API サーフェスのサブセットのバイナリ互換性を保証します。バイナリ互換性を保証しない部分は、@UnstableApi でマークされます。この区別を明確にするために、不安定な API シンボルの使用は、@OptIn アノテーションが付いていない限り、lint エラーを生成します。

ExoPlayer v2 から Media3 に移行した後、不安定な API lint エラーが多数発生する可能性があります。このため、Media3 が ExoPlayer v2 よりも「安定性が低い」ように見える場合があります。これは誤りです。Media3 API の「不安定」な部分は、ExoPlayer v2 API サーフェス全体と同じレベルの安定性を持ち、安定した Media3 API サーフェスの保証は ExoPlayer v2 ではまったく利用できません。違いは、lint エラーが安定性レベルの違いを警告するようになったことです。

不安定な API lint エラーの処理

不安定な API の lint エラーを処理する方法は 2 つあります。

  • 安定した API に切り替えて同じ結果を得る。
  • 不安定な API の使用を継続し、使用方法に @OptIn アノテーションを付けます。

    import androidx.annotation.OptIn
    import androidx.media3.common.util.UnstableApi
    
    @OptIn(UnstableApi::class)
    fun functionUsingUnstableApi() {
      // Do something useful.
    }
    

    なお、使用すべきでない kotlin.OptIn アノテーションもあります。この目的のためには、androidx.annotation.OptIn アノテーションを使用することが重要です。

    スクリーンショット: Optin アノテーションを追加する方法
    図 2: Android Studio で @androidx.annotations.OptIn アノテーションを追加する

パッケージ全体をオプトインするには、package-info.java を追加します。

@OptIn(markerClass = UnstableApi.class)
package name.of.your.package;

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

プロジェクト全体をオプトインするには、lint.xml で特定の lint エラーを抑制します。詳しくは、UnstableApi アノテーションの JavaDoc をご覧ください。

サポート終了 API

非推奨の API の呼び出しは、Android Studio では取り消し線が引かれている場合があります。このような呼び出しは、適切な代替値に置き換えることをおすすめします。この記号にカーソルを合わせると、代わりに使用する API を示す JavaDoc が表示されます。

スクリーンショット: 非推奨のメソッドの代替を含む JavaDoc を表示する方法
図 3: Android Studio の JavaDoc ツールチップに、非推奨のシンボルの代替候補が表示されます。

コードサンプルとデモアプリ