处理音频输出的变化

用户期望能够控制音频应用程序的音量。标准行为包括能够使用音量控件(设备上的按钮或旋钮或 UI 中的滑块),以及在使用过程中断开耳机等外设连接时避免突然大声播放。

使用音量控件

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

Android 使用单独的音频流来播放音乐、闹钟、通知、来电铃声、系统声音、通话音量和 DTMF 音调。这允许用户独立控制每个流的音量。

默认情况下,按下音量控制会修改活动音频流的音量。如果您的应用程序当前没有播放任何内容,则按下音量键会调整音乐音量(或 Android 9 之前的铃声音量)。

除非您的应用程序是闹钟,否则您应该使用 AudioAttributes.USAGE_MEDIA 用途播放音频。

为了确保音量控制调节的是正确的音频流,您应该调用 setVolumeControlStream() 并传入与您的属性匹配的音频流类型,您可以从 AudioAttributes.getVolumeControlStream 中获取。

Kotlin

setVolumeControlStream(AudioManager.STREAM_MUSIC)

Java

setVolumeControlStream(AudioManager.STREAM_MUSIC);

在您的应用程序生命周期中进行此调用,通常是在控制您的媒体的活动或片段的 onResume() 方法中。这将把音量控制与 STREAM_MUSIC 连接起来,只要目标活动或片段可见。

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

在极少数情况下,您可以以编程方式设置音频流的音量。例如,当您的应用程序替换现有 UI 时。不建议这样做,因为 Android AudioManager 会将同一类型的音频流混合在一起。这些方法会更改使用该音频流的所有应用程序的音量。避免使用它们。

使用固定音量设备

某些设备(例如 Chromebook)具有音量控制,但不允许应用程序使用上面描述的 AudioManager 方法来更改音频流的级别。这些设备称为固定音量设备。您可以通过调用 isVolumeFixed() 来确定您的应用程序是否运行在固定音量设备上。

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

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

不要制造噪音

用户在享受 Android 设备音频方面有很多选择。大多数设备都有内置扬声器、用于有线耳机的耳机插孔,许多设备还具有蓝牙连接功能并支持 A2DP 音频。

当耳机拔掉或蓝牙设备断开连接时,音频流会自动重新路由到内置扬声器。如果您以较高的音量听音乐,这可能会造成噪音。

用户通常期望包含带有屏幕上播放控制的音乐播放器的应用程序在这种情况下暂停播放。其他应用程序(如没有控制的应用程序)应该继续播放。用户可以使用设备的硬件控制调节音量。

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

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