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)에 따르면, 앱은 각 독립 재생 소스에 대해 하나의 MediaSession을 생성해야 합니다. 한 번에 하나의 세션만 활성화될 수 있으며, 새 세션은 자동으로 이전 세션을 비활성화합니다.
MediaSession과 MediaBrowserService의 결합은 완전한 미디어 관리 주기를 제공합니다. 서비스는 콘텐츠 트리(재생 목록, 카탈로그)를 제공하고, 세션은 탐색 및 재생 명령을 수락합니다. 이는 Google이 음악 플레이어, 팟캐스트 및 오디오북에 권장하는 아키텍처입니다.
MediaBrowserService는 알림과 함께 포그라운드 서비스로 실행되어 Activity가 소멸되어도 앱이 계속 작동하도록 보장합니다. 이는 앱이 최소화되어도 계속 재생되어야 하는 오디오 플레이어에게 중요합니다.
세션은 두 가지 유형의 상호 작용을 지원합니다. MediaController(클라이언트 측)에서 명령을 수신하고 PlaybackState를 통해 상태를 브로드캐스트합니다. MediaController는 동일한 프로세스 또는 별도의 앱에 있을 수 있으며, 시스템은 SessionToken을 통해 요청을 라우팅합니다.
사용자가 Android 음성 어시스턴트를 호출하여 “다음 트랙 재생”이라고 말하면, 시스템은 MediaBrowserService에 대한 연결을 통해 활성 MediaSession을 찾고 ACTION_SKIP_TO_NEXT 명령을 보냅니다. 앱의 Callback은 onSkipToNext() 호출을 수신하고 PlaybackState를 업데이트합니다.
setPlaybackState()를 통한 PlaybackState 업데이트는 연결된 모든 MediaController 인스턴스에 즉시 알립니다. 시스템 미디어 센터, Bluetooth 기기 및 Android Auto가 동시에 업데이트를 수신하며, 일반적인 조건에서 지연은 50ms를 초과하지 않습니다.
PlaybackState에는 주요 상태 플래그(isPlaying, 위치, 속도, 사용 가능한 작업(play, pause, seek, stop))가 포함됩니다. PlaybackState가 올바르게 채워지지 않으면 시스템은 앱이 지원하는 명령을 알 수 없으며 해당 이벤트를 보내지 않습니다.
메타데이터(MediaMetadata)는 현재 트랙에 대한 정보(제목, 아티스트, 앨범 아트 URI)로 상태를 보완합니다. Android Auto 및 웨어러블 기기는 MediaMetadata를 사용하여 화면에 정보를 표시합니다. Google I/O 2024에 따르면, 올바른 MediaMetadata 채우기는 타사 런처에서 앱의 가시성을 40% 향상시킵니다.
MediaSession 아키텍처는 각각 고유한 역할을 하는 네 개의 상호 연결된 구성 요소로 구성됩니다. 완전한 시스템 통합을 위해 개발자는 네 가지를 모두 구현해야 합니다.
세션은 서비스 또는 Activity의 onCreate 메서드에서 MediaSessionCompat(context, tag)를 호출하여 생성됩니다. 생성 후 setFlags(FLAG_HANDLES_MEDIA_BUTTONS | FLAG_HANDLES_TRANSPORT_CONTROLS)를 호출해야 합니다. onDestroy에서는 시스템 리소스를 해제하기 위해 release()가 호출됩니다.
잘못된 생명 주기 관리는 흔한 실수 중 하나입니다. release()를 호출하지 않으면 세션이 시스템에 남아 있고 다음 앱이 오래된 상태를 받을 수 있습니다. Android 13+는 세션 누수에 대해 Logcat 경고를 표시합니다.
기본 통합은 세션을 생성하고 Callback을 구현하는 것으로 시작됩니다. API 14에서 35까지 모든 Android 버전에 통합 API를 제공하는 AndroidX 미디어 라이브러리의 MediaSessionCompat을 사용하는 것이 좋습니다.
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()가 구현되어 있으면 시스템이 직접 호출합니다. 헤드셋 버튼을 한 번 누르면 KEYCODE_MEDIA_PLAY_PAUSE가 전송되며, 플레이어는 상태를 전환해야 합니다.
최신 Bluetooth 헤드폰은 최대 5개의 버튼(볼륨 +/-, play/pause, next, previous)을 가질 수 있습니다. 각 버튼은 고유한 KeyEvent를 생성하며, Callback에서 올바르게 처리해야 합니다. 많은 헤드셋에서 play/pause를 두 번 누르면 일반적으로 다음 트랙(ACTION_SKIP_TO_NEXT)으로 건너뛰는 것으로 해석됩니다.
Android 호환성 정의 문서(2026)에 따르면, 미디어 콘텐츠가 있는 모든 앱은 KEYCODE_MEDIA_PLAY_PAUSE를 올바르게 처리해야 합니다. 이 요구 사항을 무시하면 Google Play의 음악 및 오디오 카테고리에서 앱 순위가 자동으로 하락합니다.
Android 11부터 시스템 미디어 센터(미디어 제어 패널)는 알림 창에 최대 5개의 최근 MediaSession을 표시합니다. 사용자는 앱을 열지 않고도 플레이어를 제어할 수 있습니다. 올바른 표시를 위해 MediaBrowserService를 구현하고 PlaybackState를 올바르게 채워야 합니다.
미디어 제어 패널에는 트랙 제목, 아티스트, 커버 아트(MediaMetadata에서), 제어 버튼(PlaybackState의 사용 가능한 작업에서)이 표시됩니다. 앱이 활성 재생 중 10초마다 한 번 이상 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 권한이 필요합니다.
자주 묻는 질문
기술적으로 가능하지만, 한 번에 하나의 세션만 활성으로 간주됩니다. setActive(true)를 호출하지 않고 새 세션을 만들면 이전 세션이 활성 상태로 유지됩니다. 앱당 하나의 세션 또는 활성 상태 전환과 함께 독립 오디오 소스당 하나의 세션을 유지하는 것이 좋습니다.
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 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.