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 の拡張機能を使用します。
  • きめ細かいアクセス制御で再生機能をアドバタイズする。
  • MediaSessionConnector と PlayerNotificationManager を削除して、アプリを簡素化します。
  • 下位互換性ありメディア互換クライアント API を使用 (MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat )

AndroidX Media3 に移行する Media API

  • ExoPlayer とその拡張機能
    これには、廃止された mediasession モジュールを除く、以前の 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.* パッケージに依存)
    MediaBrowserCompat または MediaControllerCompat を使用するクライアント コードを移行して、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
    • アスタリスク(*)を使用しているすべてのワイルドカード インポート ステートメントを置き換え、完全修飾インポート ステートメントを使用します。ワイルドカード インポート ステートメントを削除し、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. スクリプトによってコードがどのように変更されたかを確認する: diff ツールを使用して、潜在的な問題を修正します(-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. import ステートメントを並べ替える: Android Studio でプロジェクトを開き、プロジェクト ビューアでパッケージ フォルダノードを右クリックして、変更されたソースファイルを含むパッケージで [Optimize imports] を選択します。

MediaSessionConnector を androidx.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.11.1"
    
  3. MediaSessionCompat を androidx.media3.session.MediaSession に置き換えます。

  4. 以前の MediaSessionCompat を作成したコードサイトで、androidx.media3.session.MediaSession.Builder を使用して MediaSession をビルドします。プレーヤーを渡して、セッション ビルダーを構築します。

    Kotlin

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player).setCallback(MySessionCallback()).build()

    Java

    ExoPlayer player = new ExoPlayer.Builder(context).build();
    mediaSession =
        new MediaSession.Builder(context, player).setCallback(new MySessionCallback()).build();

  5. アプリの必要に応じて MySessionCallback を実装します。これは省略可能です。コントローラがプレーヤーにメディア アイテムを追加できるようにするには、MediaSession.Callback.onAddMediaItems() を実装します。このクラスは、下位互換性のある方法で再生用のメディア アイテムをプレーヤーに追加する、さまざまな最新および以前の API メソッドを提供します。これには、Media3 コントローラの MediaController.set/addMediaItems() メソッドと、以前の API の TransportControls.prepareFrom*/playFrom* メソッドが含まれます。onAddMediaItems の実装例については、セッション デモアプリの PlaybackService をご覧ください。

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

    Kotlin

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

    Java

    if (mediaSession != null) {
      mediaSession.getPlayer().release();
      mediaSession.release();
      mediaSession = null;
    }

Media3 の MediaSessionConnector 機能

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

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare() は内部で呼び出されます)
QueueNavigator ForwardingSimpleBasePlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

MediaBrowserService を MediaLibraryService に移行する

AndroidX Media3 では、MediaBrowserServiceCompat に代わる MediaLibraryService が導入されています。MediaLibraryService とそのスーパークラス MediaSessionService の JavaDoc には、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.11.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 をビルドします。

      Kotlin

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

      Java

      mediaLibrarySession =
          new MediaLibrarySession.Builder(context, player, new 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.MediaItem と MediaMetadataCompat の代わりに androidx.media3.common.MediaItem を使用しています。レガシークラスに関連付けられているコードの一部は、それに応じて変更するか、Media3 MediaItem にマッピングする必要があります。

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

PlayerNotificationManager を削除する

MediaLibraryService はメディア通知を自動的にサポートし、MediaLibraryService または MediaSessionService を使用している場合は PlayerNotificationManager を削除できます。

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

MediaLibraryService.updateNotification() をオーバーライドすることで、アプリは通知の投稿と、必要に応じてフォアグラウンドでのサービスの開始/停止を完全に制御できます。

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

AndroidX Media3 では、MediaBrowser が MediaController/Player インターフェースを実装し、メディア ライブラリのブラウジングに加えて、メディア再生の制御にも使用できます。以前の環境で MediaBrowserCompat と MediaControllerCompat を作成する必要があった場合は、Media3 で MediaBrowser のみを使用することで同じことができます。

MediaBrowser をビルドして、サービスへの接続が確立されるまで待機できます。

Kotlin

scope.launch {
  val sessionToken = SessionToken(context, ComponentName(context, "MusicService"))
  browser =
    MediaBrowser.Builder(context, sessionToken)
      .setListener(BrowserListener())
      .buildAsync()
      .await()
}

Java

SessionToken sessionToken =
    new SessionToken(context, new ComponentName(context, "MusicService"));
ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken)
        .setListener(new BrowserListener())
        .buildAsync();

メディア セッションでの再生を制御するを参照して、バックグラウンドでの再生を制御する 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 エラーを処理する

@OptIn を使用して不安定な API の Java と Kotlin の使用にアノテーションを付ける方法については、これらの lint エラーに関するトラブルシューティングのセクションをご覧ください。

非推奨 API

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

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

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