音声フォーカスの管理

複数の Android アプリで同じ出力ストリームに対して音声を再生できる すべてが一緒にミックスされます。これは ユーザーに不快感を与える可能性があります。すべての脅威を 同時に再生することもできますが、Android ではオーディオ あります。一度に 1 つのアプリのみが音声フォーカスを保持できる、という概念です。

アプリで音声を出力する必要がある場合は、音声フォーカスをリクエストします。期限が切れたとき サウンドを再生できます。ただし、音声フォーカスを取得した後は、 最後まで残しておくことができます。別のアプリがフォーカスをリクエストした場合、 保留音をプリエンプトします。その場合、アプリは一時停止します。 音量を下げて、新しい音源がはっきり聞こえるようにします。

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)以降では、 確認します。あるアプリが音声フォーカスをリクエストしている間に、別のアプリが音声フォーカスを保持し、 再生中のアプリは強制的にフェードアウトします。Pod の フェードアウトにより、アプリ間の遷移がよりスムーズになります。

このフェードアウトの動作は、次の条件を満たす場合に発生します。

  1. 現在再生中の 1 つ目のアプリは、以下の条件をすべて満たしています。

  2. 2 つ目のアプリが AudioManager.AUDIOFOCUS_GAIN で音声フォーカスをリクエストします。

これらの条件が満たされると、オーディオ システムは最初のアプリがフェードアウトします。Google フェードアウトが終了すると、システムは最初のアプリにフォーカス喪失を通知します。アプリの アプリが音声フォーカスを再度リクエストするまで、プレーヤーはミュートされたままになります。

既存の音声フォーカスの動作

音声フォーカスの切り替えを伴う以下のようなケースについても把握しておく必要があります。

自動ダッキング

自動ダッキング(1 つのアプリの音量を一時的に下げて、 は Android 8.0(API レベル 26)で導入されました。

システムにダッキングを実装すると、ダッキングを実装する必要がなくなります。 説明します。

自動ダッキングは、音声通知が現在再生しているアプリからフォーカスを取得した場合にも発生します。通知の再生開始はダッキング ランプの最後と同期されます。

自動ダッキングは、次の条件を満たす場合に発生します。

  1. 現在再生中の 1 つ目のアプリは、以下の条件をすべて満たしています。

  2. 2 つ目のアプリは、音声フォーカスをリクエストします。 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

これらの条件が満たされると、オーディオ システムはデバイスのアクティブなプレーヤーすべてをダッキングします。 2 つ目のアプリはフォーカスされています。2 つ目のアプリが放棄されたとき うまくいきません1 つ目のアプリはフォーカスを喪失しても通知されません。 何もする必要はありません。

なお、ユーザーが他の曲を聴いているときは自動ダッキングは行われません。 番組の一部を聞き逃す可能性があるため、音声コンテンツに注意を払います。たとえば 運転ルートの音声案内がダッキングされません。

電話の着信時に現在の音声の再生をミュートする

一部のアプリは正常に動作せず、通話中に音声を再生し続けます。 このような状況では、ユーザーは問題のあるアプリを見つけてミュートするか、 必要があります。これを防ぐために、他の参加者の音声はミュートされます。 アプリを操作できるようになります。この機能が呼び出されるのは、 電話の着信を受け、アプリが以下の条件を満たしている場合。

  • アプリに 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_GAINAUDIOFOCUS_GAIN_TRANSIENTAUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCKAUDIOFOCUS_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)

リスナーを設定するには 2 つの方法があります。1 つは 使用します。ハンドラとは、リスナーが実行されるスレッドです。もし ハンドラを指定していない場合、main に関連付けられたハンドラ 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
        }
    }
}

Java

// 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() を呼び出して 。

遅延されたフォーカスの取得を処理するには、 OnAudioFocusChangeListener と、onAudioFocusChange() コールバック メソッドを 必要な動作を実装し、次を呼び出してリスナーを登録します。 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
}

Java

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)

Java

// 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 コールバックを受け取ります。再生を再開するには、 再生トランスポート コントロールを押すなど、明示的な操作を行う必要があります 表示することもできます。

次のコード スニペットは、 OnAudioFocusChangeListener とその onAudioFocusChange() コールバック。なお、 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
        }
    }
}

Java

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()
}

Java

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

ユーザーが再生を再開した場合に遅延停止が発生しないよう、 任意の状態に対する mHandler.removeCallbacks(mDelayedStopRunnable) できます。たとえば、コールバックの onPlay()removeCallbacks() を呼び出します。 onSkipToNext() などを指定できます。また、サービスの サービスで使用されているリソースをクリーンアップする際の onDestroy() コールバック。