このセクションでは、メディア プレーヤー アプリをメディア コントローラ(UI 用)とメディア セッション(実際のプレーヤー用)に分割する方法を説明します。また、オーディオ アプリに適したクライアント / サーバー設計と、動画プレーヤーに適したシングル アクティビティ設計の、2 種類のメディアアプリのアーキテクチャを紹介します。さらに、メディアアプリでハードウェア コントロールに応答したり、音声出力ストリームを使用する他のアプリと連携したりする方法も示します。
プレーヤーと UI
通常、音声または動画を再生するマルチメディア アプリには、次の 2 つの部分があります。
- デジタル メディアを取り込んで、動画や音声として再生するプレーヤー
- プレーヤーを操作したり、必要に応じてプレーヤーの状態を表示したりするためのトランスポート コントロールを備えた UI
Android では、独自のプレーヤーを最初から構築することも、次のオプションから選択することもできます。
- MediaPlayer クラスを使用することで、ごく一般的な音声 / 動画形式とデータソースをサポートするシンプルなプレーヤー向けの基本機能を利用できます。
- ExoPlayer は、下位レベルの Android オーディオ API を公開するオープンソース ライブラリです。
MediaPlayer
では使用できない DASH や HLS ストリーミングなどの高性能な機能をサポートしています。ExoPlayer のコードをカスタマイズすることで、新しいコンポーネントの追加を容易に行えます。ExoPlayer は、Android バージョン 4.1 以降でのみ使用できます。
メディア セッションとメディア コントローラ
UI とプレーヤーでは任意の API を使用できますが、両者間でのやりとりの本質部分は、基本的にはどのメディア プレーヤー アプリでも同じです。Android フレームワークでは、「メディア セッション」と「メディア コントローラ」という 2 つのクラスが定義されています。これにより、メディア プレーヤー アプリを作成するための明確な構造が規定されます。
メディア セッションとメディア コントローラの間の通信では、標準のプレーヤー アクション(再生、一時停止、停止など)に対応する定義済みのコールバックと、アプリ固有の特別な動作を定義する拡張可能なカスタム呼び出しが使用されます。
メディア セッション
プレーヤーとのすべての通信は、メディア セッションにより処理されます。そのため、アプリの他の部分からプレーヤーの API は見えません。プレーヤーは、それを制御するメディア セッションのみから呼び出されます。
メディア セッションには、プレーヤーの状態(再生、一時停止)と、再生中のコンテンツに関する情報が保持されます。また、1 つのセッションで、1 つ以上のメディア コントローラからのコールバックを受信できます。これにより、アプリの UI だけでなく、Wear OS や Android Auto を実行中のコンパニオン デバイスからも、プレーヤーの制御が可能になります。コールバックに応答するロジックには一貫性が必要です。どのクライアント アプリで開始された MediaSession
コールバックであっても、それに対する応答は同じにする必要があります。
メディア コントローラ
メディア コントローラにより UI が分離されます。UI のコードでは、メディア コントローラとのみやりとりし、プレーヤー自体とはやりとりしません。メディア コントローラにより、トランスポート コントロールのアクションがメディア セッションへのコールバックに変換されます。また、メディア セッションの状態が変わるたびに、メディア コントローラはメディア セッションからコールバックを受信します。これにより、関連する UI を自動的に更新する仕組みを実現できます。なお、メディア コントローラから一度に接続できるメディア セッションは 1 つのみです。
メディア コントローラとメディア セッションを使用すると、ランタイムにさまざまなインターフェースやプレーヤーをデプロイできます。アプリを実行するデバイスの機能に応じて、アプリの外観やパフォーマンスを個別に変更できます。
動画アプリとオーディオ アプリの比較
動画の再生中は、目と耳の両方を使います。一方、音声の再生中は、耳は使いながらも、同時に別のアプリで作業を行えます。このような使い方の違いにより、設計も異なってきます。
動画アプリ
動画アプリには、コンテンツを表示するためのウィンドウが必要です。そのため、通常は単一の Android アクティビティとして実装します。そのアクティビティの一部に動画の画面を表示します。
オーディオ アプリ
オーディオ プレーヤーでは、常に UI を表示しておく必要はありません。音声の再生を開始した後は、プレーヤーをバックグラウンド タスクとして実行できます。ユーザーは別のアプリに切り替えて、作業しながら聴き続けることができます。
Android でこの設計を実装するには、UI 用のアクティビティとプレーヤー用のサービスという、2 つのコンポーネントを使用してオーディオ アプリを構築します。ユーザーが別のアプリに切り替えても、サービスならバックグラウンドでの実行が可能です。オーディオ アプリの 2 つのパートを別々のコンポーネントに分けることで、それぞれをより効率的に実行できます。UI の存続時間は、UI なしで長時間実行可能なプレーヤーに比べて短いのが一般的です。
サポート ライブラリには、このクライアント / サーバー アプローチを実装するために、MediaBrowserService
と MediaBrowser
の 2 つのクラスが用意されています。サービス コンポーネントは、メディア セッションとそのプレーヤーを含む MediaBrowserService
のサブクラスとして実装されます。UI とメディア コントローラを含むアクティビティには、MediaBrowserService
と通信する MediaBrowser
を含めるようにします。
MediaBrowserService
を使用すると、アプリの UI アクティビティにまったくアクセスすることなく、コンパニオン デバイス(Android Auto や Android Wear など)からアプリの検出、アプリへの接続、コンテンツのブラウジング、再生のコントロールを簡単に行うことができます。実際には、1 つの MediaBrowserService
が同時に複数のアプリから接続されている可能性があり、各アプリには固有の MediaController
があります。そのため、MediaBrowserService
を提供するアプリでは、複数の同時接続を処理できるようにします。
メディアアプリと Android オーディオ インフラストラクチャ
適切に設計されたメディアアプリでは、音声を再生する他のアプリと「うまく連携」できるようになっています。つまり、デバイス上の他の音声アプリとスマートフォンを共有できるような作りになっています。また、デバイス上のハードウェア コントロールにも応答できるように作られています。
このような動作の詳細については、音声出力の制御をご覧ください。
media-compat ライブラリ
media-compat ライブラリには、音声や動画を再生するアプリの作成に役立つクラスが含まれています。これらのクラスは、Android 2.3(API レベル 9)以降を搭載するデバイスに対応しています。また、他の Android の機能と連携させることができ、使いやすく快適な Android 環境を構築できます。
メディア セッションとメディア コントローラの推奨される実装は、media-compat サポート ライブラリで定義されている MediaSessionCompat
クラスと MediaControllerCompat
クラスです。これらは、Android 5.0(API レベル 21)で導入された、以前のバージョンの MediaSession
クラスと MediaController
クラスに置き換わるものです。compat クラスでは、機能は同じですが、記述する API が 1 つのみで済むため、アプリの開発が容易になります。このライブラリには下位互換性があり、可能な範囲でメディア セッションのメソッドが古いプラットフォーム バージョンの同等のメソッドに変換されます。
稼働中のアプリで古いクラスを使用している場合は、compat クラスに更新することをおすすめします。compat バージョンを使用すれば、registerMediaButtonReceiver()
の呼び出しと RemoteControlClient
からのメソッドをすべて削除できます。
パフォーマンスの測定
Android 8.0(API レベル 26)以降では、一部のメディアクラスで getMetrics()
メソッドを使用できます。これにより、PersistableBundle
オブジェクトが返されます。このオブジェクトには、属性と値をマッピングした形式の、設定とパフォーマンスの情報が含まれています。getMetrics()
メソッドは、以下のメディアクラスに定義されています。
MediaPlayer.getMetrics()
MediaRecorder.getMetrics()
MediaCodec.getMetrics()
MediaExtractor.getMetrics()
指標はインスタンスごとに個別に収集され、インスタンスの存続期間中保持されます。利用可能な指標がない場合、メソッドからは null が返されます。実際に返される指標は、クラスによって異なります。