오디오 출력 변경 처리
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
사용자는 오디오 앱의 볼륨을 제어할 수 있기를 기대합니다. 표준 동작에는 볼륨 컨트롤 (기기의 버튼 또는 노브 또는 UI의 슬라이더)을 사용하고 사용 중에 헤드폰과 같은 주변기기가 연결 해제되면 갑자기 소리가 크게 재생되지 않도록 하는 기능이 포함됩니다.
볼륨 컨트롤 사용
곡 사이에 플레이어가 일시 중지되거나 현재 게임 위치에 사용되는 음악이 없는 경우에도 사용자가 게임이나 음악 앱의 볼륨 키를 누르면 볼륨이 변경됩니다.
Android는 음악, 알람, 알림, 수신 전화 벨소리 장치, 시스템 소리, 통화 볼륨, DTMF 신호음 등의 재생에 별도의 오디오 스트림을 사용합니다. 이를 통해 사용자는 각 스트림의 볼륨을 독립적으로 제어할 수 있습니다.
기본적으로 볼륨 컨트롤을 누르면 활성 오디오 스트림의 볼륨이 변경됩니다. 앱이 현재 재생되지 않는 경우 볼륨 키를 눌러 음악 볼륨 (또는 Android 9 이전의 벨소리 장치 볼륨)을 조정합니다.
앱이 알람 시계가 아니라면 AudioAttributes.USAGE_MEDIA
를 사용하여 오디오를 재생해야 합니다.
볼륨 컨트롤이 올바른 스트림을 조정하도록 하려면 AudioAttributes.getVolumeControlStream
에서 검색할 수 있는 속성과 일치하는 스트림 유형으로 전달하는 setVolumeControlStream()
을 호출해야 합니다.
Kotlin
setVolumeControlStream(AudioManager.STREAM_MUSIC)
자바
setVolumeControlStream(AudioManager.STREAM_MUSIC);
앱 수명 주기 중에 일반적으로 미디어를 제어하는 활동 또는 프래그먼트의 onResume()
메서드에서 이 호출을 진행합니다. 이렇게 하면 타겟 활동 또는 프래그먼트가 표시될 때마다 볼륨 컨트롤이 STREAM_MUSIC
에 연결됩니다.
프로그래밍 방식으로 스트림 볼륨 제어
드문 경우이지만 오디오 스트림의 볼륨을 프로그래밍 방식으로 설정할 수도 있습니다. 예를 들면 앱이 기존 UI를 대체하는 경우입니다. Android AudioManager
가 동일한 유형의 모든 오디오 스트림을 함께 혼합하므로 이 방법은 권장하지 않습니다.
다음 메서드는 스트림을 사용하는 모든 앱의 볼륨을 변경하므로 다음은 사용하지 않는 것이 좋습니다.
고정 볼륨 기기 작업
Chromebook 및 Android Automotive OS 자동차와 같은 일부 기기에는 볼륨 컨트롤이 있지만, 앱이 앞에서 설명한 AudioManager
메서드를 사용하여 오디오 스트림의 레벨을 변경하도록 허용하지 않습니다. 이를 고정 볼륨 기기라고 합니다. isVolumeFixed()
를 호출하여 앱이 현재 고정 볼륨 기기에서 실행 중인지 확인할 수 있습니다.
오디오 앱은 동일한 스트림에서 재생할 수 있는 다른 앱과 출력 볼륨의 균형을 맞출 수 있어야 합니다.
고정 볼륨 기기에서 앱은 자체 볼륨 컨트롤을 적절한 setVolume()
메서드에 연결해야 합니다.
큰 소리 차단
사용자는 다양한 방법으로 Android 기기에서 오디오를 즐길 수 있습니다. 대부분의 기기에는 내장 스피커와 유선 헤드셋용 헤드폰 잭이 있으며 블루투스 연결과 A2DP 오디오 지원 기능도 제공됩니다.
헤드셋의 전원 플러그를 뽑거나 블루투스 기기를 분리하면 오디오 스트림의 경로가 내장 스피커로 자동 변경됩니다. 높은 볼륨으로 음악을 듣는 경우 갑자기 큰 소리가 날 수 있습니다.
이때 사용자는 일반적으로 화면 재생 컨트롤이 있는 음악 플레이어가 포함된 앱이 재생을 일시중지할 것으로 기대합니다. 컨트롤이 포함되지 않은 게임과 같은 다른 앱은 계속해서 재생됩니다. 사용자는 기기의 하드웨어 컨트롤을 사용하여 볼륨을 조정할 수 있습니다.
오디오 출력이 내장 스피커로 다시 전환되면 시스템은 ACTION_AUDIO_BECOMING_NOISY
인텐트를 브로드캐스트합니다. 오디오를 재생할 때마다 이 인텐트를 수신 대기하는 BroadcastReceiver
를 만들어야 합니다. Receiver는 다음과 같습니다.
Kotlin
private class BecomingNoisyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) {
// Pause the playback
}
}
}
자바
private class BecomingNoisyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
// Pause the playback
}
}
}
재생을 시작할 때 Receiver를 등록하고 중지할 때 등록을 취소합니다.
이 가이드에서 설명하는 대로 앱을 디자인하면 onPlay()
및 onStop()
미디어 세션 콜백에 이러한 호출이 나타납니다.
Kotlin
private val intentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
private val myNoisyAudioStreamReceiver = BecomingNoisyReceiver()
private val callback = object : MediaSessionCompat.Callback() {
override fun onPlay() {
registerReceiver(myNoisyAudioStreamReceiver, intentFilter)
}
override fun onStop() {
unregisterReceiver(myNoisyAudioStreamReceiver)
}
}
Java
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private BecomingNoisyReceiver myNoisyAudioStreamReceiver = new BecomingNoisyReceiver();
MediaSessionCompat.Callback callback = new
MediaSessionCompat.Callback() {
@Override
public void onPlay() {
registerReceiver(myNoisyAudioStreamReceiver, intentFilter);
}
@Override
public void onStop() {
unregisterReceiver(myNoisyAudioStreamReceiver);
}
}
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-27(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-07-27(UTC)"],[],[],null,["# Handling changes in audio output\n\nUsers expect to be able to control the volume of an audio app. Standard behavior\nincludes the ability to use the volume controls (either buttons or knobs on the\ndevice or sliders in the UI), and to avoid suddenly playing out loud if a\nperipheral like headphones is disconnected while in use.\n\nUsing the volume controls\n-------------------------\n\nWhen a user presses a volume key in a game or music app the volume should\nchange, even if the player is paused between songs or there's no music for the\ncurrent game location.\n\nAndroid uses separate audio streams for playing music, alarms,\nnotifications, the incoming call ringer, system sounds, in-call volume, and DTMF\ntones. This allows users to control the volume of each stream independently.\n\nBy default, pressing the volume control modifies the volume of the active audio\nstream. If your app isn't currently playing anything, hitting the volume keys\nadjusts the music volume (or the ringer volume before Android 9).\n\nUnless your app is an alarm clock, you should play audio with usage\n[AudioAttributes.USAGE_MEDIA](/reference/android/media/AudioAttributes#USAGE_MEDIA).\n\nTo ensure that volume controls adjust\nthe correct stream, you should call\n[setVolumeControlStream()](/reference/android/app/Activity#setVolumeControlStream(int))\npassing in the stream type matching your attributes that you can retrieve from\n[AudioAttributes.getVolumeControlStream](/reference/android/media/AudioAttributes#getVolumeControlStream()). \n\n### Kotlin\n\n```kotlin\nsetVolumeControlStream(AudioManager.STREAM_MUSIC)\n```\n\n### Java\n\n```java\nsetVolumeControlStream(AudioManager.STREAM_MUSIC);\n```\n\nMake this call in your app's lifecycle, typically from the `onResume()`\nmethod of the activity or fragment that controls your media. This connects\nthe volume controls to `STREAM_MUSIC` whenever the target activity or fragment\nis visible.\n\n### Controlling stream volume programmatically\n\nIn rare cases, you can set the volume of an audio stream programmatically. For\nexample, when your app replaces an existing UI. This is not recommended because\nthe Android `AudioManager` mixes all audio streams of the same type together.\nThese methods change the volume of every app that uses the stream. Avoid using\nthem:\n\n- [adjustStreamVolume()](/reference/android/media/AudioManager#adjustStreamVolume(int, int, int))\n- [adjustSuggestedStreamVolume()](/reference/android/media/AudioManager#adjustSuggestedStreamVolume(int, int, int))\n- [adjustVolume()](/reference/android/media/AudioManager#adjustVolume(int, int))\n- [setStreamVolume()\n setStreamVolume()](/reference/android/media/AudioManager#setStreamVolume(int, int, int))\n- [setStreamSolo()](/reference/android/media/AudioManager#setStreamSolo(int, boolean))\n- [setStreamMute()](/reference/android/media/AudioManager#setStreamMute(int, boolean))\n\nWorking with fixed-volume devices\n---------------------------------\n\nSome devices (like Chromebooks and Android Automotive OS cars) have volume\ncontrols but don't allow apps to use the `AudioManager` methods described\nearlier to change the level of an audio stream. These are called *fixed-volume*\ndevices. You can discover if your app is running on a fixed-volume device by\ncalling [`isVolumeFixed()`](/reference/android/media/AudioManager#isVolumeFixed()).\n\nAn audio app should provide the ability to balance\nits output volume with other apps that might be playing on the same stream.\nOn *fixed-volume* devices, the app should connect its own volume controls to the\nappropriate `setVolume()` method:\n\n| Player | Method |\n|-------------|---------------------------------------------------------------------------------------------------------------------------------|\n| AudioTrack | [AudioTrack.setVolume()](/reference/android/media/AudioTrack#setVolume(float)) |\n| MediaPlayer | [MediaPlayer.setVolume()](/reference/android/media/MediaPlayer#setVolume(float, float)) |\n| ExoPlayer | Use `SimpleExoPlayer.setVolume()` which sets the volume of the underlying AudioTrack. |\n| Web | Set the [`volume`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volume) property of the `HTMLMediaElement` |\n\nDon't be noisy\n--------------\n\nUsers have a number of alternatives when it comes to enjoying the audio from\ntheir Android devices. Most devices have a built-in speaker, headphone jacks for\nwired headsets, and many also feature Bluetooth connectivity and support for\nA2DP audio.\n\nWhen a headset is unplugged or a Bluetooth device disconnected, the audio stream\nautomatically reroutes to the built-in speaker. If you listen to music at a high\nvolume, this can be a noisy surprise.\n\nUsers usually expect apps that include a music player with onscreen playback\ncontrols to pause playback in this case. Other apps, like games that don't\ninclude controls, should keep playing. The user can adjust the volume with the\ndevice's hardware controls.\n\nWhen audio output switches back to the built-in speaker the system broadcasts an [ACTION_AUDIO_BECOMING_NOISY](/reference/android/media/AudioManager#ACTION_AUDIO_BECOMING_NOISY)\nintent. You should create a [BroadcastReceiver](/reference/android/content/BroadcastReceiver)\nthat listens for this intent whenever you're playing audio. Your receiver should look like this: \n\n### Kotlin\n\n```kotlin\nprivate class BecomingNoisyReceiver : BroadcastReceiver() {\n\n override fun onReceive(context: Context, intent: Intent) {\n if (intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) {\n // Pause the playback\n }\n }\n}\n```\n\n### Java\n\n```java\nprivate class BecomingNoisyReceiver extends BroadcastReceiver {\n @Override\n public void onReceive(Context context, Intent intent) {\n if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {\n // Pause the playback\n }\n }\n}\n```\n\nRegister the receiver when you begin playback, and unregister it when you stop.\nIf you design your app as we describe in this guide, these calls should appear\nin the `onPlay()` and `onStop()` media session callbacks. \n\n### Kotlin\n\n```kotlin\nprivate val intentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)\nprivate val myNoisyAudioStreamReceiver = BecomingNoisyReceiver()\n\nprivate val callback = object : MediaSessionCompat.Callback() {\n\n override fun onPlay() {\n registerReceiver(myNoisyAudioStreamReceiver, intentFilter)\n }\n\n override fun onStop() {\n unregisterReceiver(myNoisyAudioStreamReceiver)\n }\n}\n```\n\n### Java\n\n```java\nprivate IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);\nprivate BecomingNoisyReceiver myNoisyAudioStreamReceiver = new BecomingNoisyReceiver();\n\nMediaSessionCompat.Callback callback = new\nMediaSessionCompat.Callback() {\n @Override\n public void onPlay() {\n registerReceiver(myNoisyAudioStreamReceiver, intentFilter);\n }\n\n @Override\n public void onStop() {\n unregisterReceiver(myNoisyAudioStreamReceiver);\n }\n}\n```"]]