오디오 포커스 관리

두 개 이상의 Android 앱이 동일한 출력 스트림으로 오디오를 재생할 수 있습니다. 시스템은 모든 것을 함께 혼합합니다 지금은 기술적으로 인상적일 경우 사용자에게는 매우 악화될 수 있습니다. 모든 동시에 음악 앱이 재생될 때 Android는 오디오, 음악, 스마트 포커스가 있습니다. 한 번에 하나의 앱만 오디오 포커스를 유지할 수 있습니다.

앱이 오디오를 출력해야 하는 경우 오디오 포커스를 요청해야 합니다. 소리를 재생할 수 있습니다. 그러나 오디오 포커스를 획득한 후에는 계속 저장할 수 있습니다. 다른 앱이 포커스를 요청할 수 있는데 오디오 포커스를 선점합니다. 이 경우 앱이 일시중지되어야 합니다. 사용자가 새로운 오디오 소스를 더 쉽게 들을 수 있도록 볼륨을 낮추세요.

Android 12 (API 수준 31) 이전에는 오디오 포커스가 시스템에서 관리되지 않습니다. 따라서 앱 개발자는 오디오 포커스 가이드라인을 준수하는 것이 좋습니다. 기기에서 오디오 포커스를 잃은 후에도 앱이 계속 큰 소리로 재생되는 경우 Android 11 (API 수준 30) 이하를 실행하는 경우 시스템에서는 이를 방지할 수 없습니다. 하지만 이러한 앱 동작은 부정적인 사용자 경험으로 이어지며 사용자가 오작동하는 앱을 제거하도록 하십시오.

잘 설계된 오디오 앱은 다음과 같은 일반적인 내용에 따라 오디오 포커스를 관리해야 합니다. 가이드라인:

  • 재생을 시작하기 직전에 requestAudioFocus()를 호출하여 다음을 확인합니다. 호출이 AUDIOFOCUS_REQUEST_GRANTED 다음으로 전화 걸기 미디어 세션의 onPlay() 콜백에 있는 requestAudioFocus()

  • 다른 앱이 오디오 포커스를 획득하면 재생을 중지 또는 일시중지하거나 볼륨을 낮춥니다 (즉, 줄일 수 있습니다.

  • 재생이 중지되는 경우 (예: 앱에 더 이상 재생할 항목이 없는 경우) 오디오 포커스를 포기합니다 사용자가 앱에서 오디오 포커스를 포기할 필요는 없습니다. 재생을 일시중지하지만 나중에 재생을 다시 시작할 수 있습니다.

  • AudioAttributes를 사용하여 설명합니다. 오디오 유형도 지정할 수 있습니다. 예를 들어 음성을 재생하는 앱의 경우 지정하다 CONTENT_TYPE_SPEECH

오디오 포커스는 이전에 작성된 Android 버전에 따라 다르게 처리됩니다. 이(가) 실행 중입니다.

Android 12 (API 수준 31) 이상
오디오 포커스는 시스템에서 관리합니다. 시스템은 다른 앱이 오디오 포커스를 요청하면 앱이 페이드 아웃되도록 설정해야 합니다. 시스템 수신 전화를 받을 때 오디오 재생도 음소거합니다.
Android 8.0 (API 수준 26)~Android 11 (API 수준 30)
오디오 포커스는 시스템에서 관리되지 않지만 오디오 포커스에 Android 8.0 (API 수준 26)부터 도입되었습니다.
Android 7.1 (API 수준 25) 이하
오디오 포커스는 시스템에서 관리되지 않으며 앱은 requestAudioFocus()abandonAudioFocus()입니다.

Android 12 이상에서 오디오 포커스

오디오 포커스를 사용하는 미디어 앱이나 게임 앱은 포커스가 손실된 후 오디오를 재생해서는 안 됩니다. Android 12 (API 수준 31) 이상에서는 시스템이 있습니다. 다른 앱에 포커스가 있는 동안 앱이 오디오 포커스를 요청하는 경우 시스템이 재생 중인 앱을 강제로 페이드 아웃합니다. 이 페이드 아웃을 사용하면 한 앱에서 다른 앱으로 이동할 때 전환이 더 원활해집니다.

이 페이드 아웃 동작은 다음 조건이 충족될 때 발생합니다.

  1. 현재 재생 중인 첫 번째 앱은 다음 기준을 모두 충족합니다.

  2. 두 번째 앱은 AudioManager.AUDIOFOCUS_GAIN를 사용하여 오디오 포커스를 요청합니다.

이러한 조건이 충족되면 오디오 시스템이 첫 번째 앱을 페이드 아웃합니다. 페이드 아웃이 끝나면 시스템은 첫 번째 앱에 포커스 손실을 알립니다. 앱의 플레이어는 앱이 오디오 포커스를 다시 요청할 때까지 음소거 상태로 유지됩니다.

기존 오디오 포커스 동작

오디오 포커스 전환과 관련된 다음과 같은 기타 사례도 알아야 합니다.

자동 볼륨 낮추기

자동 볼륨 낮추기 (특정 앱의 오디오 레벨을 일시적으로 낮춰서 명확하게 들릴 수 있음)가 Android 8.0 (API 수준 26)에서 도입되었습니다.

시스템에서 볼륨 낮추기를 구현하도록 하면 볼륨 낮추기를 구현할 필요가 없습니다. 있습니다.

자동 볼륨 낮추기는 오디오 알림이 재생 중인 앱에서 포커스를 가져올 때도 발생합니다. 알림 재생의 시작은 볼륨 낮추기 램프의 끝과 동기화됩니다.

자동 볼륨 낮추기는 다음 조건이 충족될 때 발생합니다.

  1. 현재 재생 중인 첫 번째 앱은 다음 기준을 모두 충족합니다.

  2. 두 번째 앱은 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

이러한 조건이 충족되면 오디오 시스템은 두 번째 앱에 포커스가 있습니다. 두 번째 앱이 이탈할 때 초점을 맞추면 저해되지 않습니다. 첫 번째 앱은 포커스를 잃을 때 알림을 받지 않으며 별도의 작업이 필요하지 않습니다.

사용자가 듣고 있는 동안에는 자동 볼륨 낮추기가 실행되지 않습니다. 사용자가 프로그램의 일부를 놓칠 수 있기 때문입니다. 예를 들어 운전 경로 음성 안내의 볼륨이 낮아지지 않습니다.

수신 전화의 현재 오디오 재생 음소거

일부 앱이 제대로 작동하지 않고 전화 통화 중에 계속 오디오를 재생합니다. 이러한 상황에서는 사용자가 통화 내용을 듣기 위해서입니다. 이를 방지하기 위해 시스템에서 앱을 표시할 수 있습니다. 시스템에서 앱이 다음 조건을 충족할 경우,

  • 앱에 AudioAttributes.USAGE_MEDIA 또는 AudioAttributes.USAGE_GAME 사용 속성
  • 앱이 오디오 포커스 (모든 포커스 게인)를 성공적으로 요청했으며 재생 중입니다. 오디오입니다.

통화 중에 앱이 계속 재생되면 통화가 끝날 때까지 재생이 음소거됩니다. 그러나 통화 중에 앱이 재생되기 시작하면 해당 플레이어는 음소거된 상태로 재생되어야 합니다.

Android 8.0~Android 11의 오디오 포커스

Android 8.0 (API 수준 26)부터 requestAudioFocus() 드림 AudioFocusRequest 매개변수를 제공해야 합니다. AudioFocusRequest 에는 앱의 오디오 컨텍스트와 기능에 관한 정보가 포함됩니다. 이 시스템은 이 정보를 사용하여 오디오 포커스의 게인과 손실을 자동으로 확장 및 축소할 수 있습니다 오디오 포커스를 해제하려면 메서드를 호출합니다. abandonAudioFocusRequest() 드림 이 인수는 AudioFocusRequest도 인수로 사용합니다. 같은 AudioFocusRequest 인스턴스는 포커스를 요청하고 포기할 때 모두 발생합니다.

AudioFocusRequest를 만들려면 다음을 사용합니다. AudioFocusRequest.Builder 포커스 요청은 항상 요청의 유형을 지정하며, 유형은 생성자에 포함됩니다. 확인할 수 있습니다 빌더의 메서드를 사용하여 합니다.

FocusGain 필드는 필수사항이고 다른 필드는 모두 선택사항입니다.

메서드참고
setFocusGain() 이 필드는 모든 요청에서 필수사항입니다. 이 함수는 Android 8.0 이전 requestAudioFocus() 호출에 사용된 durationHint: AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT, AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 또는 AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE입니다.
setAudioAttributes() AudioAttributes은 앱의 사용 사례를 설명합니다. 이 시스템은 앱이 오디오 포커스를 획득하거나 상실할 때 이를 확인합니다. 특성 는 스트림 유형의 개념을 대체합니다. Android 8.0 (API 수준 26) 이상에서 볼륨 컨트롤 이외의 작업에 대한 스트림 유형은 지원 중단되었습니다. 사용 포커스 요청의 오디오 플레이어에서 사용하는 것과 동일한 속성( 참조).

AudioAttributes.Builder를 사용하여 이 방법을 사용하여 합니다.

지정하지 않으면 AudioAttributes의 기본값은 AudioAttributes.USAGE_MEDIA입니다.

setWillPauseWhenDucked() 다른 앱이 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 포커스가 있는 앱이 일반적으로 onAudioFocusChange() 콜백을 실행할 수 있고 볼륨 낮추기를 수행합니다. 오디오북을 재생하기 전에 재생을 일시중지해야 하는 경우 setWillPauseWhenDucked(true)를 호출하여 OnAudioFocusChangeListener, 자동 볼륨 낮추기를 지원합니다.
setAcceptsDelayedFocusGain() 다른 앱이 포커스를 잠그면 오디오 포커스 요청이 실패할 수 있습니다. 이 메서드는 지연된 포커스 게를 가능하게 합니다. 지연 시간은 포커스를 획득할 수 있게 되면 비동기적으로 포커스를 획득할 수 있습니다.

지연된 포커스 게인은 AudioManager.OnAudioFocusChangeListener 오디오 요청에 포함시켜야 합니다. 콜백을 수신해야 포커스가 허용되었음을 알 수 있습니다.

setOnAudioFocusChangeListener() OnAudioFocusChangeListener는 요청에 willPauseWhenDucked(true) 또는 setAcceptsDelayedFocusGain(true)가 포함되어 있습니다.

리스너를 설정하는 방법에는 두 가지가 있습니다. 하나는 핸들러 인수를 지정합니다. 핸들러는 리스너가 실행되는 스레드입니다. 만약 핸들러는 기본 핸들러와 연결된 핸들러, Looper가 사용됩니다.

다음 예는 AudioFocusRequest.Builder를 사용하여 빌드하는 방법을 보여줍니다. AudioFocusRequest를 호출하여 오디오 포커스를 요청하고 포기합니다.

Kotlin

// initializing variables for audio focus and playback management
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
    setAudioAttributes(AudioAttributes.Builder().run {
        setUsage(AudioAttributes.USAGE_GAME)
        setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        build()
    })
    setAcceptsDelayedFocusGain(true)
    setOnAudioFocusChangeListener(afChangeListener, handler)
    build()
}
val focusLock = Any()

var playbackDelayed = false
var playbackNowAuthorized = false

// requesting audio focus and processing the response
val res = audioManager.requestAudioFocus(focusRequest)
synchronized(focusLock) {
    playbackNowAuthorized = when (res) {
        AudioManager.AUDIOFOCUS_REQUEST_FAILED -> false
        AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {
            playbackNow()
            true
        }
        AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> {
            playbackDelayed = true
            false
        }
        else -> false
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
override fun onAudioFocusChange(focusChange: Int) {
    when (focusChange) {
        AudioManager.AUDIOFOCUS_GAIN ->
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false
                    resumeOnFocusGain = false
                }
                playbackNow()
            }
        AudioManager.AUDIOFOCUS_LOSS -> {
            synchronized(focusLock) {
                resumeOnFocusGain = false
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying()
                playbackDelayed = false
            }
            pausePlayback()
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // ... pausing or ducking depends on your app
        }
    }
}

자바

// initializing variables for audio focus and playback management
audioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE);
playbackAttributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
        .setAudioAttributes(playbackAttributes)
        .setAcceptsDelayedFocusGain(true)
        .setOnAudioFocusChangeListener(afChangeListener, handler)
        .build();
final Object focusLock = new Object();

boolean playbackDelayed = false;
boolean playbackNowAuthorized = false;

// requesting audio focus and processing the response
int res = audioManager.requestAudioFocus(focusRequest);
synchronized(focusLock) {
    if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
        playbackNowAuthorized = false;
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        playbackNowAuthorized = true;
        playbackNow();
    } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
        playbackDelayed = true;
        playbackNowAuthorized = false;
    }
}

// implementing OnAudioFocusChangeListener to react to focus changes
@Override
public void onAudioFocusChange(int focusChange) {
    switch (focusChange) {
        case AudioManager.AUDIOFOCUS_GAIN:
            if (playbackDelayed || resumeOnFocusGain) {
                synchronized(focusLock) {
                    playbackDelayed = false;
                    resumeOnFocusGain = false;
                }
                playbackNow();
            }
            break;
        case AudioManager.AUDIOFOCUS_LOSS:
            synchronized(focusLock) {
                resumeOnFocusGain = false;
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            synchronized(focusLock) {
                // only resume if playback is being interrupted
                resumeOnFocusGain = isPlaying();
                playbackDelayed = false;
            }
            pausePlayback();
            break;
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            // ... pausing or ducking depends on your app
            break;
        }
    }
}

자동 볼륨 낮추기

Android 8.0 (API 수준 26)에서 다른 앱이 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 시스템이 볼륨을 낮추고 복원할 수 있음 앱의 onAudioFocusChange() 콜백을 호출하지 않고 이 메서드를 호출할 수 있습니다.

자동 볼륨 낮추기는 음악 및 동영상 재생 시 허용 가능한 동작이지만 음성 콘텐츠를 재생할 때는 유용하지 않습니다. 오디오북 앱 이 경우에는 앱이 대신 일시중지되어야 합니다.

볼륨 낮추기 요청이 있을 때 앱이 일시중지되도록 하려면 볼륨을 줄이는 대신 다음과 같이 OnAudioFocusChangeListener를 만듭니다. 원하는 일시중지/재개 동작을 구현하는 onAudioFocusChange() 콜백 메서드 setOnAudioFocusChangeListener()를 호출하여 리스너를 등록하고 setWillPauseWhenDucked(true) 자동 볼륨 낮추기를 실행하지 않고 콜백을 사용하도록 시스템에 알립니다.

포커스 획득 지연

오디오 포커스에 대한 요청을 허용할 수 없는 경우가 있는데, 이는 포커스가 '잠김' 다른 앱에서 발생한 트래픽입니다. 이 경우 requestAudioFocus()AUDIOFOCUS_REQUEST_FAILED를 반환합니다. 이 경우 오디오 재생의 증폭이 확보되지 않았기 때문에 집중하세요.

앱이 포커스 요청을 처리하도록 허용하는 setAcceptsDelayedFocusGain(true) 메서드 있습니다. 이 플래그를 설정하면 포커스가 잠겨 있을 때 요청이 수행됨 AUDIOFOCUS_REQUEST_DELAYED를 반환합니다. 오디오를 잠그는 조건이 포커스가 더 이상 존재하지 않는 경우(예: 전화 통화가 끝나면, 시스템은 대기 중인 포커스 요청을 허용하고 onAudioFocusChange()를 호출하여 있습니다.

지연된 포커스 게인을 처리하려면 OnAudioFocusChangeListeneronAudioFocusChange() 콜백 메서드로 대체 원하는 동작을 구현하고 setOnAudioFocusChangeListener()

Android 7.1 이하의 오디오 포커스

통화 시 requestAudioFocus() 드림 재생 시간 힌트를 지정해야 합니다. 현재 포커스를 보유하고 재생 중인 다른 앱에 의해 적용됩니다.

  • 오디오를 재생하려는 경우 영구적인 오디오 포커스 (AUDIOFOCUS_GAIN)를 요청합니다. 재생될 때처럼 멀어질 수 있으며 재생 중지를 클릭합니다.
  • 재생될 것으로 예상되면 일시적인 포커스 (AUDIOFOCUS_GAIN_TRANSIENT)를 요청합니다. 오디오가 잠시 동안만 발생하며 이전 소유자가 일시중지할 것으로 예상하는 경우 있습니다.
  • 볼륨 낮추기를 사용하여 일시적인 포커스 요청 (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)를 사용하여 오디오가 재생될 것으로 예상됨을 나타냅니다. 이전 포커스 소유자가 계속 사용해도 되는지 '오리'라고 말하면 (낮춰) 오디오 출력을 생성합니다. 두 오디오 출력이 혼합됨 오디오 스트림에 삽입해야 합니다. 볼륨 낮추기는 특히 오디오 스트림이 간헐적으로 끊김 없이 수신될 수 있습니다.

requestAudioFocus() 메서드에는 AudioManager.OnAudioFocusChangeListener도 필요합니다. 이 리스너는 미디어 세션을 소유하는 동일한 활동 또는 서비스에서 생성된 것입니다. 그것은 앱이 수신하는 콜백 onAudioFocusChange()를 구현합니다. 다른 앱이 오디오 포커스를 획득하거나 포기합니다.

다음 스니펫은 스트림에 영구적인 오디오 포커스를 요청합니다. STREAM_MUSIC를 처리하고 처리할 OnAudioFocusChangeListener를 등록합니다. 오디오 포커스의 후속 변경사항을 생성합니다. (변경 리스너에 대해서는 오디오 포커스 변경에 응답).

Kotlin

audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
lateinit var afChangeListener AudioManager.OnAudioFocusChangeListener

...
// Request audio focus for playback
val result: Int = audioManager.requestAudioFocus(
        afChangeListener,
        // Use the music stream.
        AudioManager.STREAM_MUSIC,
        // Request permanent focus.
        AudioManager.AUDIOFOCUS_GAIN
)

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

자바

AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
AudioManager.OnAudioFocusChangeListener afChangeListener;

...
// Request audio focus for playback
int result = audioManager.requestAudioFocus(afChangeListener,
                             // Use the music stream.
                             AudioManager.STREAM_MUSIC,
                             // Request permanent focus.
                             AudioManager.AUDIOFOCUS_GAIN);

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // Start playback
}

재생이 끝나면 abandonAudioFocus()

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

자바

// Abandon audio focus when playback complete
audioManager.abandonAudioFocus(afChangeListener);

이렇게 하면 포커스가 더 이상 필요하지 않다고 시스템에 알리고 연결된 OnAudioFocusChangeListener 일시적인 포커스를 요청한 경우 이렇게 하면 일시중지되거나 볼륨을 낮춘 앱에 계속 재생될 수 있음을 알리거나 볼륨 복원하기

오디오 포커스 변경에 응답

앱이 오디오 포커스를 획득하면 다른 앱이 오디오 포커스를 해제할 수 있어야 합니다. 자체 오디오 포커스를 요청합니다. 이 경우 앱은 은 onAudioFocusChange() 드림 메서드(AudioFocusChangeListener) 앱이 requestAudioFocus()를 호출할 때 지정한 값

onAudioFocusChange()에 전달된 focusChange 매개변수는 실로 엄청난 변화입니다. 대응 포커스를 획득하는 앱이 사용하는 지속 시간 힌트에 해당될 수 있습니다. 앱은 다음과 같아야 합니다. 도움이 될 수 있습니다

일시적인 포커스 손실
포커스 변경이 일시적 (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 또는 AUDIOFOCUS_LOSS_TRANSIENT), 앱의 볼륨을 낮춰야 합니다( 자동 볼륨 낮추기) 또는 재생을 일시중지하지만 나머지는 동일한 상태를 유지합니다

오디오 포커스가 일시적으로 손실된 동안에도 변화를 계속 모니터링해야 합니다. 다시 시작 버튼을 누르면 일반 재생을 다시 시작할 준비를 해야 집중하세요. 차단 앱이 포커스를 포기하면 콜백이 수신됨 (AUDIOFOCUS_GAIN) 이 시점에서 볼륨을 보통 수준으로 복원할 수 있습니다. 재생 다시 시작

영구적인 포커스 손실
오디오 포커스 손실이 영구적이면 (AUDIOFOCUS_LOSS) 다른 앱이 발생합니다. 있습니다. 앱에서 재생을 즉시 일시중지해야 합니다. 절대로 재생을 일시중지하지 않기 때문입니다. AUDIOFOCUS_GAIN 콜백을 수신합니다. 재생을 다시 시작하려면 사용자가 재생 전송 컨트롤 누르기와 같은 명시적인 작업을 실행해야 함 할 수 있습니다.

다음 코드 스니펫은 OnAudioFocusChangeListeneronAudioFocusChange() 콜백 이 Handler를 사용하여 오디오 영구 손실 시 중지 콜백 지연 집중하세요.

Kotlin

private val handler = Handler()
private val afChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
    when (focusChange) {
        AudioManager.AUDIOFOCUS_LOSS -> {
            // Permanent loss of audio focus
            // Pause playback immediately
            mediaController.transportControls.pause()
            // Wait 30 seconds before stopping playback
            handler.postDelayed(delayedStopRunnable, TimeUnit.SECONDS.toMillis(30))
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
            // Pause playback
        }
        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
            // Lower the volume, keep playing
        }
        AudioManager.AUDIOFOCUS_GAIN -> {
            // Your app has been granted audio focus again
            // Raise volume to normal, restart playback if necessary
        }
    }
}

자바

private Handler handler = new Handler();
AudioManager.OnAudioFocusChangeListener afChangeListener =
  new AudioManager.OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
      if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
        // Permanent loss of audio focus
        // Pause playback immediately
        mediaController.getTransportControls().pause();
        // Wait 30 seconds before stopping playback
        handler.postDelayed(delayedStopRunnable,
          TimeUnit.SECONDS.toMillis(30));
      }
      else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
        // Pause playback
      } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
        // Lower the volume, keep playing
      } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
        // Your app has been granted audio focus again
        // Raise volume to normal, restart playback if necessary
      }
    }
  };

핸들러는 다음과 같은 Runnable을 사용합니다.

Kotlin

private var delayedStopRunnable = Runnable {
    mediaController.transportControls.stop()
}

자바

private Runnable delayedStopRunnable = new Runnable() {
    @Override
    public void run() {
        getMediaController().getTransportControls().stop();
    }
};

사용자가 재생을 다시 시작해도 지연된 중지가 시작되지 않도록 하려면 모든 상태에 대한 응답 mHandler.removeCallbacks(mDelayedStopRunnable) 있습니다. 예를 들어 콜백의 onPlay()에서 removeCallbacks()를 호출합니다. onSkipToNext() 등입니다. 서비스의 서비스에서 사용하는 리소스를 삭제할 때 onDestroy() 콜백이 호출됩니다.