MediaSessionは、メディアコンテンツの再生を管理し、外部デバイスと統合するためのAndroidフレームワークコンポーネントです。Bluetoothヘッドセット、ヘッドフォン、Android Auto、システムメディアセンターとのインタラクションのための統一インターフェースを提供します。Android Developers Guide, 2026によると、MediaSessionは非推奨のRemoteControlClientを置き換え、システムと同期するプレーヤーに必須です。
重要なポイント
MediaSessionは、アプリがメディアアクティビティを宣言し、外部ソースから制御コマンドを受け取ることを可能にするAndroidのシステムコンポーネントです。ユーザーがBluetoothヘッドセットの再生ボタンを押すと、システムはこのイベントをアクティブなMediaSessionに渡し、アプリはCallbackを介して応答します。
Android 5.0以前は、この目的のためにRemoteControlClientが使用されていましたが、十分な柔軟性がなく、Android Auto、スマートウォッチ、スマートスピーカーなどの最新のシナリオをサポートしていませんでした。MediaSessionはAPI 21で導入され、オーディオおよびビデオ再生を行うすべてのAndroidアプリの事実上の標準となりました。
Androidデベロッパードキュメント(2026年)によると、アプリは独立した再生ソースごとに1つのMediaSessionを作成する必要があります。任意の時点でアクティブにできるセッションは1つだけで、新しいセッションは自動的に前のセッションを非アクティブにします。
MediaSessionとMediaBrowserServiceの組み合わせにより、完全なメディア管理サイクルが実現します。サービスはコンテンツツリー(プレイリスト、カタログ)を提供し、セッションはナビゲーションと再生コマンドを受け入れます。これはGoogleが音楽プレーヤー、ポッドキャスト、オーディオブックに推奨するアーキテクチャです。
MediaBrowserServiceは通知付きのフォアグラウンドサービスとして実行され、Activityが破棄されてもアプリが動作し続けることを保証します。これは、アプリが最小化されている間も再生を続ける必要があるオーディオプレーヤーにとって重要です。
セッションは2種類のインタラクションをサポートします。MediaController(クライアント側)からコマンドを受信し、PlaybackStateを介して状態をブロードキャストします。MediaControllerは同じプロセス内または別のアプリ内に存在でき、システムはSessionTokenを介してリクエストをルーティングします。
ユーザーがAndroid音声アシスタントを呼び出して“次のトラックを再生”と言うと、システムはMediaBrowserServiceへの接続を介してアクティブなMediaSessionを見つけ、ACTION_SKIP_TO_NEXTコマンドを送信します。アプリのCallbackはonSkipToNext()呼び出しを受信し、PlaybackStateを更新します。
setPlaybackState()によるPlaybackStateの更新は、接続されているすべてのMediaControllerインスタンスに即座に通知されます。システムメディアセンター、Bluetoothデバイス、Android Autoが同時に更新を受信し、通常の条件下での遅延は50ミリ秒を超えません。
PlaybackStateには主要な状態フラグ(isPlaying、位置、速度、利用可能なアクション(再生、一時停止、シーク、停止))が含まれます。PlaybackStateが適切に設定されていないと、システムはアプリがどのコマンドをサポートしているかを認識できず、対応するイベントを送信しません。
メタデータ(MediaMetadata)は、現在のトラックに関する情報(タイトル、アーティスト、アルバムアートURI)で状態を補完します。Android AutoとウェアラブルデバイスはMediaMetadataを使用して画面に情報を表示します。Google I/O 2024によると、MediaMetadataを適切に設定すると、サードパーティのランチャーでのアプリの可視性が40%向上します。
MediaSessionアーキテクチャは4つの相互接続されたコンポーネントで構成され、それぞれが独自の役割を果たします。完全なシステム統合には、開発者は4つすべてを実装する必要があります。
セッションは、サービスまたはActivityのonCreateメソッドでMediaSessionCompat(context, tag)を呼び出して作成されます。作成後、setFlags(FLAG_HANDLES_MEDIA_BUTTONS | FLAG_HANDLES_TRANSPORT_CONTROLS)を呼び出す必要があります。onDestroyでは、システムリソースを解放するためにrelease()が呼び出されます。
ライフサイクル管理の誤りはよくある間違いの1つです。release()を呼び出さないと、セッションがシステムに残り、次のアプリが古い状態を受け取る可能性があります。Android 13+はセッションリークに対してLogcat警告を表示します。
基本的な統合は、セッションを作成しCallbackを実装することから始まります。AndroidXメディアライブラリのMediaSessionCompatを使用することをお勧めします。これは、API 14から35までのすべてのAndroidバージョンに統一APIを提供します。
class MusicService : Service() {
private lateinit var mediaSession: MediaSessionCompat
private lateinit var stateBuilder: PlaybackStateCompat.Builder
override fun onCreate() {
super.onCreate()
mediaSession = MediaSessionCompat(this, "MusicService")
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)
mediaSession.setCallback(MediaSessionCallback())
updatePlaybackState(false)
}
private inner class MediaSessionCallback : MediaSessionCompat.Callback() {
override fun onPlay() {
updatePlaybackState(true)
}
override fun onPause() {
updatePlaybackState(false)
}
}
private fun updatePlaybackState(isPlaying: Boolean) {
stateBuilder = PlaybackStateCompat.Builder()
.setState(
if (isPlaying) PlaybackStateCompat.STATE_PLAYING
else PlaybackStateCompat.STATE_PAUSED,
AudioTrackCompat.CURRENT_POSITION_NOT_SET,
1.0f
)
.setActions(
PlaybackStateCompat.ACTION_PLAY
or PlaybackStateCompat.ACTION_PAUSE
or PlaybackStateCompat.ACTION_SKIP_TO_NEXT
or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
)
mediaSession.setPlaybackState(stateBuilder.build())
}
override fun onDestroy() {
mediaSession.release()
super.onDestroy()
}
}
この例では、MusicServiceタグとメディアボタン操作用のフラグを持つMediaSessionが作成されています。CallbackはonPlayとonPauseを実装し、PlaybackStateを更新します。setActionsメソッドは、システムがロック画面とメディアセンターに表示する利用可能なアクションを宣言します。
private fun setMetadata(title: String, artist: String) {
val metadata = MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, 300000L)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, albumArtUrl)
.build()
mediaSession.setMetadata(metadata)
}
メタデータはトラックが変更されるたびに更新する必要があります。システムはMETADATA_KEY_TITLEとMETADATA_KEY_ARTISTを使用して、車載Bluetoothディスプレイやウェアラブルデバイスに情報を表示します。カバーアートURIが設定されていない場合、プレーヤーは灰色のプレースホルダーを表示します。
BluetoothヘッドセットはAVRCP 1.6+プロファイルを介してコマンドを送信します。AndroidはこれらのコマンドをACTION_MEDIA_BUTTONインテントとしてブロードキャストし、FLAG_HANDLES_MEDIA_BUTTONSフラグが設定されている場合、MediaSessionがインターセプトします。
ユーザーがBluetoothヘッドフォンの再生ボタンを押すと、システムはKEYCODE_MEDIA_PLAYコードのKeyEventを作成し、CallbackのonMediaButtonEventメソッドにディスパッチします。CallbackにonPlay()が実装されている場合、システムは直接呼び出します。ヘッドセットボタンを1回押すとKEYCODE_MEDIA_PLAY_PAUSEが送信され、プレーヤーは状態を切り替える必要があります。
最近のBluetoothヘッドフォンには最大5つのボタン(音量+/−、再生/一時停止、次へ、前へ)がある場合があります。各ボタンは独自のKeyEventを生成し、Callbackで適切に処理する必要があります。多くのヘッドセットでは、再生/一時停止のダブルプレスは通常、次のトラック(ACTION_SKIP_TO_NEXT)へのスキップとして解釈されます。
Android互換性定義ドキュメント(2026年)によると、メディアコンテンツを持つすべてのアプリはKEYCODE_MEDIA_PLAY_PAUSEを適切に処理する必要があります。この要件を無視すると、Google Playの音楽およびオーディオカテゴリでアプリの評価が自動的に低下します。
Android 11以降、システムメディアセンター(メディアコントロールパネル)は、通知シェードに最大5つの最近のMediaSessionを表示します。ユーザーはアプリを開かずにプレーヤーを制御できます。正しく表示するには、MediaBrowserServiceを実装し、PlaybackStateを適切に設定する必要があります。
メディアコントロールパネルには、トラックのタイトル、アーティスト、カバーアート(MediaMetadataから)、コントロールボタン(PlaybackStateの利用可能なアクションから)が表示されます。アプリがアクティブな再生中に少なくとも10秒に1回PlaybackStateを更新しない場合、メディアセンターはパネルからセッションを非表示にします。
private fun startForegroundService() {
val notification = NotificationCompat.Builder(this, "media_channel")
.setSmallIcon(R.drawable.ic_play)
.setContentTitle("再生中")
.setContentText(currentTrackTitle)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.sessionToken)
)
.build()
startForeground(1001, notification)
}
MediaStyle通知はsessionTokenを介してMediaSessionにリンクし、標準のメディアボタンを表示します。MediaStyleがない場合、通知はコントロールボタンのない通常のアラートとして表示されます。Android 13+では、表示に明示的なPOST_NOTIFICATIONS権限が必要です。
よくある質問
技術的には可能ですが、任意の時点でアクティブと見なされるセッションは1つだけです。setActive(true)を呼び出さずに新しいセッションを作成すると、前のセッションがアクティブのままになります。アプリごとに1つのセッション、またはアクティブ状態の切り替えを行う独立したオーディオソースごとに1つのセッションを持つことをお勧めします。
MediaSessionはAudio Focusを自動的に管理しません。これは別のメカニズムです。onPlayコマンドを受信すると、開発者はAudioManagerを介してAudioFocusを個別にリクエストし、フォーカスを失った場合はセッションを介して再生を一時停止する必要があります。
FLAG_HANDLES_MEDIA_BUTTONSとFLAG_HANDLES_TRANSPORT_CONTROLSフラグが設定されているか確認してください。また、セッションがアクティブ(setActive(true))であることを確認してください。Android 12+では、メディアボタンはMediaSessionを介してのみ機能します。古いregisterMediaButtonEventReceiverはサポートされていません。
基本的なボタン処理には不要です。ただし、Android Auto、Wear OS、システムメディアセンターとの統合にはMediaBrowserServiceが必要です。Googleは、長時間のオーディオ再生を行うすべてのアプリでMediaBrowserServiceを実装することを推奨しています。
adb shell dumpsys media_sessionコマンドを使用して、アクティブなセッション、そのCallback、PlaybackStateを表示します。このユーティリティは、タグ、アクティビティステータス、最後の既知の状態とともに、登録されているすべてのセッションを表示します。デバッグに便利なツールです。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。