メディアアプリのアーキテクチャの概要

このセクションでは、メディア プレーヤー アプリをメディア コントローラ(UI 用)とメディア セッション(実際のプレーヤー用)に分離する方法について説明します。ここでは、メディアアプリ アーキテクチャとして、オーディオ アプリに適したクライアント/サーバー設計と、動画プレーヤー向けのシングル アクティビティ設計の 2 つについて説明します。また、メディアアプリをハードウェア コントロールに応答させ、音声出力ストリームを使用する他のアプリと連携する方法についても説明します。

プレーヤーと UI

通常、音声または動画を再生するマルチメディア アプリには、次の 2 つの部分があります。

  • デジタル メディアを取り込んで、動画や音声としてレンダリングするプレーヤー
  • プレーヤーを実行し、必要に応じてプレーヤーの状態を表示するためのトランスポート コントロールを備えた UI

ui-and-player

Android では、独自のプレーヤーを一から構築することも、次のオプションを選択することもできます。

  • MediaPlayer クラスは、最も一般的な音声/動画形式とデータソースをサポートするシンプルなプレーヤー用の基本機能を提供します。
  • ExoPlayer は、MediaCodecAudioTrack などの下位レベルのメディア フレームワーク コンポーネント上に構築されたオープンソース ライブラリです。ExoPlayer は、MediaPlayer では利用できない DASH などの高性能機能をサポートしています。ExoPlayer のコードをカスタマイズして、新しいコンポーネントを簡単に追加できます。ExoPlayer は、Android バージョン 4.1 以降でのみ使用できます。

メディア セッションとメディア コントローラ

UI とプレーヤーの API は任意ですが、2 つの要素間のインタラクションの性質は、基本的にすべてのメディア プレーヤー アプリで同じです。Android フレームワークは、メディア セッションとメディア コントローラの 2 つのクラスを定義しています。これらは、メディア プレーヤー アプリを作成するための明確な構造を規定します。

メディア セッションとメディア コントローラは、標準のプレーヤー アクション(再生、一時停止、停止など)に対応する定義済みのコールバックと、アプリに固有の特別な動作を定義するために使用する拡張可能なカスタム呼び出しを使用して相互に通信します。

controller-and-session

メディア セッション

プレーヤーとのすべての通信は、メディア セッションにより処理されます。これにより、プレーヤーの API がアプリの他の部分から見えなくなり、プレーヤーを制御するメディア セッションからのみ呼び出されます。

セッションはプレーヤーの状態(再生中/一時停止状態)と再生中のコンテンツに関する情報を保持します。セッションは、1 つ以上のメディア コントローラからコールバックを受信できます。これにより、アプリの UI や、Wear OS や Android Auto を実行しているコンパニオン デバイスからプレーヤーを制御できるようになります。コールバックに応答するロジックには一貫性が必要です。どのクライアント アプリがコールバックを開始したかにかかわらず、MediaSession コールバックに対するレスポンスは同じである必要があります。

メディア コントローラ

メディア コントローラにより UI が分離されます。UI コードはメディア コントローラとのみ通信し、プレーヤー自体とは通信しません。メディア コントローラは、トランスポート コントロールのアクションをメディア セッションへのコールバックに変換します。また、セッション状態が変化するたびに、メディア セッションからのコールバックを受信します。これにより、関連する UI を自動的に更新するメカニズムが提供されます。メディア コントローラが一度に接続できるメディア セッションは 1 つのみです。

メディア コントローラとメディア セッションを使用する場合、実行時にさまざまなインターフェースやプレーヤーをデプロイできます。アプリの外観やパフォーマンスは、アプリを実行するデバイスの機能に応じて個別に変更できます。

動画アプリとオーディオ アプリの比較

動画の再生中は、目と耳の両方を使います。音声を再生しているとき、音声は聞こえますが、同時に別のアプリを操作することもできます。このような使い方の違いにより、設計も異なってきます。

動画アプリ

動画アプリには、コンテンツを表示するためのウィンドウが必要です。そのため、動画アプリは通常、単一の Android アクティビティとして実装します。動画が表示される画面はアクティビティの一部です。

動画プレーヤー アクティビティ

オーディオ アプリ

オーディオ プレーヤーでは、常に UI を表示しておく必要はありません。オーディオの再生を開始すると、プレーヤーはバックグラウンド タスクとして実行できます。ユーザーは、別のアプリに切り替えて、聞き続けながら作業できます。

この設計を Android に実装するには、UI 用のアクティビティとプレーヤーのサービスという 2 つのコンポーネントを使用してオーディオ アプリを作成します。ユーザーが別のアプリに切り替えると、そのサービスをバックグラウンドで実行できます。オーディオ アプリの 2 つの部分を別々のコンポーネントに分解することで、それぞれが独立して効率的に動作します。プレーヤーは UI なしで長時間実行されるため、UI は通常、短寿命です。

音声アクティビティと BrowserService

サポート ライブラリには、このクライアント/サーバー アプローチを実装するための 2 つのクラス(MediaBrowserServiceMediaBrowser)が用意されています。サービス コンポーネントは、メディア セッションとそのプレーヤーを含む MediaBrowserService のサブクラスとして実装されます。UI とメディア コントローラを備えたアクティビティには、MediaBrowserService と通信する MediaBrowser を含める必要があります。

MediaBrowserService を使用すると、アプリの UI アクティビティにまったくアクセスすることなく、コンパニオン デバイス(Android Auto や Wear など)でアプリの検出、接続、コンテンツのブラウジング、再生の制御を簡単に行えるようになります。実際には、複数のアプリが同時に同じ MediaBrowserService に接続され、各アプリが独自の MediaController を持つことがあります。MediaBrowserService を提供するアプリは、複数の同時接続を処理できる必要があります。

メディアアプリと Android オーディオ インフラストラクチャ

適切に設計されたメディアアプリとは、音声を再生する他のアプリと「うまく連携する」必要があります。スマートフォンを共有し、デバイス上の音声を使用する他のアプリと連携できるようにする必要があります。また、デバイスのハードウェア コントロールにも応答する必要があります。

plays-with-others

このような動作の詳細については、音声出力の制御をご覧ください。

media-compat ライブラリ

media-compat ライブラリには、音声や動画を再生するアプリの作成に役立つクラスが含まれています。これらのクラスは、Android 2.3(API レベル 9)以降を搭載しているデバイスと互換性があります。また、他の Android 機能と連携して、快適で使い慣れた Android エクスペリエンスを実現します。

メディア セッションとメディア コントローラの推奨される実装は、media-compat サポート ライブラリで定義されているクラス MediaSessionCompatMediaControllerCompat です。これらのクラスは、Android 5.0(API レベル 21)で導入された以前のバージョンの MediaSession クラスと MediaController クラスに代わるものです。compat クラスは同じ機能を提供しますが、1 つの API に書き込むだけで済むため、アプリの開発が容易になります。このライブラリは下位互換性に対応しており、メディア セッション メソッドを古いプラットフォーム バージョンの同等のメソッド(使用可能な場合)に変換しています。

古いクラスを使用している機能するアプリがすでにある場合は、compat クラスに更新することをおすすめします。互換バージョンを使用すると、registerMediaButtonReceiver() へのすべての呼び出しと、RemoteControlClient のメソッドを削除できます。

パフォーマンスの測定

Android 8.0(API レベル 26)以降では、一部のメディアクラスで getMetrics() メソッドを使用できます。属性と値のマップとして表現される、構成とパフォーマンスの情報を含む PersistableBundle オブジェクトを返します。getMetrics() メソッドは、以下のメディアクラスに定義されています。

指標はインスタンスごとに収集され、インスタンスの存続する間維持されます。使用可能な指標がない場合、メソッドは null を返します。実際に返される指標は、クラスによって異なります。