处理音频输出的变化

用户希望能够控制音频应用的音量。标准行为包括能够使用音量控件(设备上的按钮或旋钮,或者界面中的滑块),以及在使用时避免因耳机等外围设备断开连接而突然播放外音。

使用音量控件

当用户按下游戏或音乐应用中的音量键时,即使播放器在歌曲之间暂停或当前游戏位置没有音乐,音量也应该发生变化。

Android 为音乐播放、闹钟、通知、来电铃声、系统提示音、通话音量和 DTMF 音使用单独的音频流。这样,用户就可以独立控制每个音频流的音量。

默认情况下,按音量控件可调节活动音频流的音量。如果您的应用当前未播放任何内容,按音量键可调节音乐音量(Android 9 之前调节的是铃声音量)。

除非您的应用是闹钟,否则您应播放使用情况为 AudioAttributes.USAGE_MEDIA 的音频。

为了确保音量控件能够调节正确音频流的音量,您应该调用 setVolumeControlStream(),并传递与您可以从 AudioAttributes.getVolumeControlStream 检索的属性相匹配的音频流类型。

Kotlin

    setVolumeControlStream(AudioManager.STREAM_MUSIC)
    

Java

    setVolumeControlStream(AudioManager.STREAM_MUSIC);
    

在应用生命周期中进行此调用,通常是从控制媒体的 Activity 或 Fragment 的 onResume() 方法中调用。每当目标 Activity 或 Fragment 可见时,此调用即会将音量控件连接到 STREAM_MUSIC

以编程方式控制音频流音量

在极少数情况下,您可以通过编程方式设置音频流的音量。例如,当您的应用替换了现有界面时。建议不要这样做,因为 Android AudioManager 会将所有相同类型的音频流混合在一起。以下方法会改变使用该音频流的每个应用的音量。请避免使用它们:

使用固定音量设备

部分设备(如 Chromebook)具有音量控件,但不允许应用使用上述 AudioManager 方法来更改音频流的音量。这些设备称为固定音量设备。您可以调用 isVolumeFixed(),了解自己的应用是否在固定容量设备上运行。

音频应用应能够将其输出音量与可能在同一音频流上播放的其他应用进行平衡。在固定音量设备上,应用应将自己的音量控件连接到下表中的相应 setVolume() 方法:

播放器 方法
AudioTrack AudioTrack.setVolume()
MediaPlayer MediaPlayer.setVolume()
ExoPlayer 使用 SimpleExoPlayer.setVolume() 设置底层 AudioTrack 的音量。

不要发出噪音

用户在 Android 设备上欣赏音频时,有多种选择。大多数设备都配有内置扬声器、有线耳机耳机插孔,很多设备还配备蓝牙连接并支持 A2DP 音频。

当耳机被拔下或蓝牙设备断开连接时,音频流会自动重新传送至内置扬声器。如果您以高音量聆听音乐,这时可能发出让人无法忍受的噪音。

在这种情况下,用户通常希望应用包含的音乐播放器具有屏幕播放控件来暂停播放。其他应用(例如不含控件的游戏)应继续播放。用户可以使用设备的硬件控制来调整音量。

当音频输出切回到内置扬声器时,系统会广播 ACTION_AUDIO_BECOMING_NOISY Intent。您应该创建一个 BroadcastReceiver,用于在每次播放音频时监听此 Intent。您的接收器应如下所示:

Kotlin

    private class BecomingNoisyReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == AudioManager.ACTION_AUDIO_BECOMING_NOISY) {
                // Pause the playback
            }
        }
    }
    

Java

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

在开始播放时注册接收器,并在停止时取消注册接收器。如果按照本指南的说明设计应用,这些调用应显示在 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);
      }
    }