데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      Android 오디오 경험을 향상시키는 필수 요소, Audio Focus

      sangho 25.06.26
      1,017 4 1
      DEVOTEE 요약
      오디오 포커스(Audio Focus)는 Android 시스템이 오디오 자원을 관리하기 위해 제공하는 필수 기능으로, 이를 올바르게 처리하지 않으면 앱 간 소리가 겹치거나 통화 중 오디오 혼선 등 사용자 경험이 크게 저하될 수 있습니다. 개발자는 `AudioManager`와 `AudioFocusRequest`를 활용해 Audio Focus를 요청·반납하며, 상태 변화에 따라 적절하게 동작을 제어해야 합니다. Audio Focus 처리는 멀티미디어 앱의 안정성과 사용자 경험을 위해 반드시 고려해야 하는 설계 요소입니다.

      오디오 출력도 자원입니다: Audio Focus를 이해하는 첫걸음

      Android 앱을 개발할 때 우리는 CPU, 메모리, 배터리, 네트워크 등 다양한 자원을 신중하게 다룹니다.

      성능을 최적화하고 사용자 경험을 향상시키기 위해 리소스를 아끼고, 효율적으로 사용하는 것은 개발자의 기본 소양이죠.

      우리가 멀티미디어 앱을 개발할 때 고려해야할 중요한 리소스가 있습니다.

      바로 오디오에 대한 권한, 즉 Audio Focus입니다. 오디오 포커스는 시스템이 관리하는 공유 리소스입니다.

      하나의 앱이 독점할 수도 있고, 다른 앱과 양보하며 사용할 수도 있습니다. 이를 무시하거나 무분별하게 다루면 사용자 경험은 순식간에 망가질 수 있습니다.

      이 글에서 우리는 Audio Focus를 하나의 소중한 리소스로 보고, 이를 올바르게 요청하고 반납하며, 다른 앱과 어떻게 공존할 수 있을지를 살펴보겠습니다.


      Audio Focus 미처리 시 발생할 수 있는 시나리오들

      Audio Focus는 단순한 권장사항이 아닙니다. 이를 무시하고 오디오 재생이나 녹음을 수행할 경우, 사용자는 다음과 같은 혼란스러운 경험을 겪을 수 있습니다.

      다음은 Android 앱 개발시 발생할 수 있는 대표적인 사례들입니다.


      1. 백그라운드 음악 앱 위에 겹치는 소리
      • 사용자는 음악 스트리밍 앱을 실행한 상태에서 팟캐스트나 라디오 앱을 켭니다.

      • 라디오 앱이 Audio Focus를 요청하지 않고 오디오를 재생합니다.

      • 결과: 두 앱의 소리가 겹쳐 재생되며 사용자 경험이 망가집니다.


      2. 전화 수신 중에도 계속 재생되는 오디오
      • 사용자가 음악을 듣고 있는 중에 전화가 옵니다.

      • 음악 앱이 재생을 중지하지 않고 계속 재생됩니다.

      • 결과: 전화 상대방의 목소리와 음악이 섞여 들리며 통화 품질이 저하됩니다.


      3. 네비게이션 앱의 음성안내 겹침
      • 내비게이션 앱이 음성 안내를 시작합니다.

      • 음악 앱은 계속 재생되고 있습니다.

      • 결과: 중요한 안내를 사용자가 놓칠 수 있습니다.


      4. 의도하지 않은 소리 녹음
      • 사용자가 음성 메모 앱을 실행해 녹음을 시작합니다.

      • 배경에서 동작 중인 음악 앱이나 라디오 앱이 여전히 재생 중입니다.

      • 결과: 원하지 않은 소리가 녹음될 수 있습니다.


      앞서 소개한 시나리오처럼 사용자가 원하지 않는 경험을 피하고, 바람직한 경험을 제공하기 위해 AudioFocus를 어떻게 활용할 수 있는지 살펴보겠습니다.

      바람직하지 못한 vs  바람직한 오디오 사용자 경험

      Audio Focus 관리 시스템의 구조와 동작 원리

      앞에서 우리는 Audio Focus에 대한 처리 없이 오디오 재생 앱을 개발할 경우에 발생할 수 있는 이슈를 살펴 보았습니다.

      이를 해결하기 위해서 Android는 Audio Focus기능을 제공합니다.

      다음 그림은 Android 앱이 Audio Focus를 사용하여 서로 다른 앱들이 오디오를 재생하는 일반적인 케이스를 보여줍니다.

      Android Audio Focus의 기본 동작 구조

      1. 음악 앱이 AudioManager에게 AudioFocus를 요청합니다.

      2. AudioManager는 음악 앱의 AudioFocus 요청을 승인(Grant) 합니다.

      3. 음악 앱은 음악을 재생합니다.

      4. 라디오 앱이 AudioManager에게 AudioFocus를 요청합니다.

      5. AudioManager는 라디오 앱의 AudioFocus 요청을 승인(Grant) 합니다.

      6. AudioManager는 현재 AudioFocus를 가지고 있는 음악 앱에게 AudioFocus를 잃었음(LOSS)을 알립니다.

      7. 음악 앱은 현재 재생 중인 음악을 일시정지 혹은 정지합니다.

      8. 라디오 앱은 재생을 시작합니다.

      9. 라디오 앱은 재생이 끝나면 사용한 AudioFocus를 반환합니다.


      Android Audio Focus System

      지금부터 위에서 사용된 요소들에 대해 간단히 살펴보겠습니다.

      기본 클래스 및 인터페이스

      • AudioManager : Audio Focus를 요청하거나 해제할 때 사용하는 Android Audio 관리를 위한 핵심 시스템 서비스 클래스

      • AudioFocusRequest : Audio Focus 요청에 대한 상세를 가진 클래스

      • AudioManager.OnAudioFocusChangeListener : Audio Focus 상태가 변경될때 호출받을 Callback 인터페이스


      요청/해제 API

      • AudioManager.requestAudioFocus(AudioFocusRequest): Int : Audio Focus를 획득 요청하며, 획득 유무 결과를 반환

        • focusGain: AudioFocusRequest 생성 시, 필수로 필요합니다. focus를 요청 시, 어떤 용도&목적을 가지는지를 의미합니다.

        focusGain 종류

        설명

        AUDIOFOCUS_GAIN

        사용시간이 길거나 예상되지 않을 경우, 다른 앱에게는 영구적인 정지를 요청하고 싶은 경우

        AUDIOFOCUS_GAIN_TRANSIENT

        짧게 사용하는 경우

        AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

        짧게 사용하며 다른 앱에게 Ducking(Volume down)을 요청하고 싶은 경우

        AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE

        짧게 사용하며 다른 앱에게 Mute혹은 정지 등을 요청하고 싶은 경우

      • AudioManager.abandonAudioFocus(AudioFocusRequest): Int

        • 획득한 Audio Focus를 해제, 해제 결과를 반환(실제 시스템에 문제가 있어 예외가 발생하는 경우를 제외하면, 항상 성공)

        • 직전에 AudioFocus를 가지고 있던(현재는 AUDIOFOCUS_LOSS_TRANSIENT_XXX 상태) AudioFocusRequest로 Focus를 넘겨준다.

          (nAudioFocusChange(AUDIOFOCUS_GAIN) 호출)


      Focus 상태 변경

      • AudioManager.OnAudioFocusChangeListener.onAudioFocusChange(int focusChange: Int) : Focus 상태가 변경되었을 때 호출됨.

        • focusChange : 변경된 focus 상태

        focusChange 종류

        설명

        AUDIOFOCUS_LOSS

        타 앱의 AUDIOFOCUS_GAIN 요청에 의해 Focus를 완전히 잃은 경우

        AUDIOFOCUS_LOSS_TRANSIENT

        타 앱의 AUDIOFOCUS_GAIN_TRANSIENT or AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 요청에 의해 일시적으로 Focus를 잃은 경우

        AUDIOFOCUS_LOSS_TRANSIENT_MAY_DUCK

        타 앱의 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 요청에 의해 일시적으로 Focus를 잃은 경우

        AUDIOFOCUS_GAIN

        * AUDIOFOCUS_LOSS(_XXX) 상태에서 Focus를 가진 앱이 Focus를 반환한 경우

        * AudioFocus 요청이 Delay된 이후 획득한 경우


      API 문서에는 없는 것들

      1. AudioFocus 요청은 언제 실패하나요?

      AudioManager를 통해 Audio Focus를 요청한다고 해서 항상 성공하는 것은 아닙니다.

      시스템은 다양한 이유로 요청을 거부할 수 있으며, 이 경우 반환값으로 AUDIOFOCUS_REQUEST_FAILED가 전달됩니다.

      이러한 실패는 흔치 않지만, 앱이 안정적으로 오디오를 재생하려면 반드시 실패 케이스에 대한 예외처리 필요합니다.


      1. 특별한 권한을 가진 앱이 Audio Focus를 사용 중인 경우

      우리는 앞서 이 상황을 전화 시나리오에서 확인했습니다. 전화는 Android 시스템에서 특수하게 처리되는 오디오 사용 케이스입니다.

      통화가 시작되면(ringtone이 울리면), 시스템은 내부적으로 Audio Focus를 잠금(Lock) 상태로 전환하며, 일반 앱의 Audio Focus 요청을 거부합니다.

      이러한 처리는 Android의 오디오 포커스 정책에 따라 구현되어 있으며, 자세한 흐름은 Android 오픈소스 코드(Android Open Source Project, AOSP)를 통해 확인할 수 있습니다.

      그렇다면 우리 앱도 Audio Focus를 잠금 상태로 점유할 수는 없을까요?

      결론부터 말씀드리자면, Android는 일반 앱에게 그런 권한을 허용하지 않습니다.

      AudioManager 클래스를 살펴보면, AUDIOFOCUS_FLAG_LOCK이라는 플래그가 존재하지만, 이 값은 숨김(hide) 처리되어 있어 일반 앱에서는 접근할 수 없습니다.

      이 플래그는 AudioPolicy를 통해 제어되며, 주로 제조사 시스템 앱이나 특수 권한을 가진 앱에서만 사용됩니다.

      즉, 일반적인 서드파티 앱이 Audio Focus를 강제로 잠그는 동작은 시스템 차원에서 허용되지 않도록 설계되어 있습니다.

      이는 다양한 앱들이 공존하는 Android 생태계에서 오디오 자원의 공정한 사용을 보장하기 위한 정책입니다.


      2. 잘못된 AudioFocus 요청

      AudioFocusRequest를 올바르게 설정하지 경우:

      • 허용되지 않은 값들을 설정한 경우

      • 설정한 값이 적절하지 않은 경우

      개발과정에서 쉽게 발견하고 수정할 수 있습니다.


      3. 시스템에서 너무 많은 AudioFocus를 사용 중인 경우

      드문 경우지만, 여러 앱이 동시에 실행 중인 상황에서 Audio Focus를 요청한 뒤 해제하지 않으면, 시스템 내부에 요청이 누적될 수 있습니다.

      이렇게 정상적으로 반납되지 않은 Focus 요청이 계속 쌓이면, 결국 시스템은 새로운 Audio Focus 요청을 거부하게 됩니다.

      아래는 관련코드와 함께, Audio Focus 요청이 거부되었을 때 시스템에서 출력된 실제 로그 메시지입니다.

      // MediaFocusControl.java
      /**
      * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
      */
      private static final int MAX_STACK_SIZE = 100;
          
      if (mFocusStack.size() > MAX_STACK_SIZE) {
          Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
          return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
      }
      2025-05-15 10:59:32.904 2369-5239 MediaFocusControl pid-2369 E Max AudioFocus stack size reached, failing requestAudioFocus()

      2. AUDIOFOCUS_LOSS_TRANSIENT_EXCLUSIVE은 없나요?

      네. 없습니다. 설명에서 본 것처럼 다른 앱이 AUDIOFOCUS_GAIN_TRANSIENT or AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 로 Focus를 요청하면

      현재 Focus를 가진 앱은 AUDIOFOCUS_LOSS_TRANSIENT 상태만 전달 받습니다.

      그럼 AUDIOFOCUS_GAIN_TRANSIENT or AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 는 무엇이 다른 걸까요?


      다음코드를 통해서 우리는 현재 Focus가 Exclusive이고 Audio Recording 중이면 Notification 소리를 내지 않는 것을 확인할 수 있습니다.

      이런 코드 일부를 통해서, 시스템 내부에서 소리를 재생해야하는 경우를 차단하고 있는 것을 알 수 있습니다.

      // https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/services/core/java/com/android/server/audio/AudioService.java#14917
      
          /**
           * @see AudioManager#shouldNotificationSoundPlay(AudioAttributes)
           */
          @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
          public boolean shouldNotificationSoundPlay(@NonNull final AudioAttributes aa) {
              super.shouldNotificationSoundPlay_enforcePermission();
              Objects.requireNonNull(aa);
              // don't play notifications if the stream volume associated with the
              // AudioAttributes of the notification record is 0 (non-zero volume implies
              // not silenced by SILENT or VIBRATE ringer mode)
              final int stream = AudioAttributes.toLegacyStreamType(aa);
              final boolean mutingFromVolume = getStreamVolume(stream) == 0;
              if (mutingFromVolume) {
                  Slog.i(TAG, "shouldNotificationSoundPlay false: muted stream:" + stream
                          + " attr:" + aa);
                  return false;
              }
              // don't play notifications if there is a user of GAIN_TRANSIENT_EXCLUSIVE audio focus
              // and the focus owner is recording
              final int uid = mMediaFocusControl.getExclusiveFocusOwnerUid();
              if (uid == -1) { // return value is -1 if focus isn't GAIN_TRANSIENT_EXCLUSIVE
                  return true;
              }
              // is the owner of GAIN_TRANSIENT_EXCLUSIVE focus also recording?
              final boolean mutingFromFocusAndRecording = mRecordMonitor.isRecordingActiveForUid(uid);
              if (mutingFromFocusAndRecording) {
                  Slog.i(TAG, "shouldNotificationSoundPlay false: exclusive focus owner recording "
                              + " uid:" + uid + " attr:" + aa);
                  return false;
              }
              return true;
          }


      Android를 개발하다보면 문서만으로는 어떤 기능을 하는지, 어떻게 동작하는지 이해되지 않는 부분을 종종 접할 수 있습니다.

      다행히 Android는 그 동안 오픈소스로 제공되어, 대부분의 동작을 실제 코드로 직접 확인할 수 있습니다.

      문서만으로 이해되지 않는 부분이 있다면, 코드에서 그 답을 찾아보는건 어떨까요?


      마무리하며...

      Audio Focus에 대한 처리는 단순히 있으면 좋은 부가기능이 아니라, 앱의 기본 동작 안정성과 사용자 경험을 지키기 위한 필수 설계 요소입니다.

      음악 플레이어, 녹음 , 음성 안내 등 앱이 소리를 재생하거나 들을 가능성이 있다면 Audio Focus를 관리해야합니다.

      지금까지 우리는 AudioFocus를 처리하지 않으면 어떤 문제가 발생할 수 있는지 그리고 어떻게 올바르게 사용하는지에 대해 알아보았습니다.

      지금 개발 중인 앱에 Audio Focus 처리가 빠져 있다면, 지금 한 번 반영해보는 것은 어떨까요?

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      sangho 님의 최신 블로그

      더보기
      동영상 기고하기