Android播放音频之按钮控制

控制音量和播放

良好的用户体验是可预测的。 如果您的应用程式播放媒体,您的使用者必须使用装置,蓝牙耳机或耳机的硬体或软体音量控制来控制应用程式的音量。

类似地,在适当和可用的情况下,播放,停止,暂停,跳过和先前的媒体回放键应当对于由应用使用的音频流执行它们各自的动作。

标识要使用的音频流

创建可预测的音频体验的第一步是了解您的应用将使用哪个音频流。

Android维护一个单独的音频流,用于播放音乐,闹钟,通知,来电铃声,系统声音,通话音量和DTMF铃声。 这主要是为了允许用户独立地控制每个流的音量。

大多数这些流仅限于系统事件,因此除非您的应用程序是替换闹钟,否则您几乎肯定会使用STREAM_MUSIC流播放您的音频。

使用硬件音量键控制音量

默认情况下,按音量控制可修改Activity音频流的音量。如果您的应用程式目前没有播放任何内容,按下音量键即可调整铃声音量。

如果你有一个游戏或音乐应用程序,那么当用户点击音量键,他们想要控制游戏或音乐的音量,即使他们当前在歌曲之间或没有音乐在当前游戏位置。

你可能会试着尝试监听音量键按下并修改音频流的音量。不要慌, Android提供了方便的setVolumeControlStream()方法来将音量键降到您指定的音频流。

确定应用程序将使用的音频流后,应将其设置为音频流目标。您应该在应用程序的生命周期中提前进行此调用 - 因为您只需在Activity生命周期中调用一次,通常应在onCreate()方法(控制您的媒体的Activity或Fragment)中调用它。这确保了只要应用程序可见,音量控制功能就像用户期望的那样。

setVolumeControlStream(AudioManager.STREAM_MUSIC);

从这一点开始,只要目标Activity或Fragment可见,按设备上的音量键就会影响您指定的音频流(在本例中为“音乐”)。

使用硬件播放控制键控制音频播放

媒体播放按钮,如播放,暂停,停止,跳过和上一个在某些手机和许多连接或无线耳机可用。每当用户按下这些硬件键之一时,系统使用ACTION_MEDIA_BUTTON操作广播一个Intent。

要响应媒体按钮点击,您需要在您的清单中注册一个BroadcastReceiver,监听此操作广播,如下所示。

<receiver android:name=".RemoteControlReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

接收器实现本身需要提取哪个键被按下来引起广播。 Intent在EXTRA_KEY_EVENT键下包括此键,而KeyEvent类包括表示每个可能的媒体按钮的列表KEYCODE_MEDIA_ *静态常量,例如KEYCODE_MEDIA_PLAY_PAUSE和KEYCODE_MEDIA_NEXT。

以下代码片段显示如何提取按下的媒体按钮,并相应地影响媒体播放。

public class RemoteControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
            KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
                // Handle key press.
            }
        }
    }
}

因为多个应用程序可能想要监听媒体按钮按下,所以您还必须以编程方式控制应用程序何时应该接收媒体按钮按下事件。

以下代码可在您的应用程序中使用AudioManager注册和注销媒体按钮事件接收器。注册后,您的广播接收器是所有媒体按钮广播的专属接收器。

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
//...

// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
//...

// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);

通常,当应用程序变为不活动或不可见时(例如在onStop()回调期间),应取消注册大多数接收者。然而,对于媒体播放应用程序来说并不简单,事实上,当应用程序不可见且无法通过屏幕上的用户界面控制时,响应媒体播放按钮是最重要的。

更好的方法是在应用程序获取并丢失音频焦点时注册和取消注册媒体按钮事件接收器。这将在下面详细介绍。

管理音频焦点

有多个应用程序可能播放音频,重要的是要考虑他们应该如何交互。为了避免每个音乐应用程序同时播放,Android使用音频焦点来控制音频播放 - 只有拥有音频焦点的应用程序才能播放音频。

在您的应用程序开始播放音频之前,应该请求并接收音频焦点。同样,它应该知道如何监听音频焦点的丢失,并在发生这种情况时适当地做出反应。

请求音频焦点

在您的应用开始播放任何音频之前,它应该保留将使用的流的音频焦点。这是通过调用requestAudioFocus()完成的,如果您的请求成功,它返回AUDIOFOCUS_REQUEST_GRANTED。

您必须指定您使用的流,以及是否需要暂时或永久的音频焦点。当您希望只在短时间内播放音频时请求暂时聚焦(例如在播放导航指示时)。当您计划在可预见的未来播放音频时请求永久音频聚焦(例如,播放音乐时)。

以下代码片段请求音乐音频流的永久音频焦点。您应该在开始播放之前立即请求音频焦点,例如当用户按下播放或下一个游戏关卡的背景音乐开始时。

AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...

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

if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    am.registerMediaButtonEventReceiver(RemoteControlReceiver);
    // Start playback.
}

一旦你完成播放,一定要调用abandonAudioFocus()。这会通知系统您不再需要焦点和注销相关联的AudioManager.OnAudioFocusChangeListener。在放弃瞬态焦点的情况下,这允许任何中断的应用程序继续播放。

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

当请求瞬态音频焦点时,您有一个附加选项:是否要启用“低音”。通常,当良好的音频应用程序失去音频焦点时,它立即使其播放静音。通过请求一个允许回避的暂时音频焦点,你告诉其他音频应用程序它们可以接受他们继续播放,只要他们降低音量,直到焦点回到他们。

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

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

低音尤其适用于间歇性使用音频流的应用程序,例如用于可听见的驾驶方向。

每当另一个应用程序请求音频焦点如上所述,其永久和瞬态(有或没有支持低音)之间的选择音频焦点由您在请求焦点时注册的侦听器接收。

处理音频焦点的丢失

如果您的应用程序可以请求音频焦点,那么当其他应用程序请求焦点时,它会反过来失去焦点。您的应用程序如何响应音频焦点的丢失取决于丢失的方式。

您请求音频焦点时注册的音频焦点更改侦听器的onAudioFocusChange()回调方法接收到描述焦点更改事件的参数。具体来说,可能的聚焦丢失事件镜像来自前一部分的聚焦请求类型 - 永久丢失,瞬时丢失和允许具有回避的瞬态。

一般来说,暂时的(临时的)音频焦点的丢失应导致您的应用程序使它的音频流静音,否则保持相同的状态。您应该继续监视音频焦点的变化,并准备在恢复焦点后暂停播放。

如果音频焦点丢失是永久性的,假设另一个应用程序现在正用于听音频,您的应用程序应该有效地结束自己。实际上,这意味着停止播放,删除媒体按钮侦听器 - 允许新的音频播放器专门处理这些事件 - 并放弃您的音频焦点。此时,您需要在恢复播放音频之前需要执行用户操作(在应用程序中按下播放)。

在下面的代码片段中,如果音频丢失是暂时的,我们暂停播放我们的媒体播放器对象,并在我们恢复焦点后重新开始播放。如果丢失是永久性的,它将取消注册我们的媒体按钮事件接收器,并停止监视音频焦点更改。

AudioManager.OnAudioFocusChangeListener afChangeListener =
    new AudioManager.OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) {
                // Pause playback
            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
                // Resume playback
            } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
                am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
                am.abandonAudioFocus(afChangeListener);
                // Stop playback
            }
        }
    };

在瞬时丢失音频焦点的情况下,允许duck(回避),而不是暂停回放。

如何Duck

低音是降低音频流输出音量的过程,使来自另一个应用程序的瞬态音频更容易听到,而不会完全中断您自己的应用程序的音频。

在以下代码段中,当我们暂时失去焦点时,降低媒体播放器对象上的音量,然后在我们重新获得焦点时将其返回到上一级。

OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
    public void onAudioFocusChange(int focusChange) {
        if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
            // Lower the volume
        } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
            // Raise it back to normal
        }
    }
};

丢失音频焦点是最重要的广播,但不是唯一的。 系统广播多个Intent,以提醒您用户的音频体验的变化。

处理音频输出硬件

当用户从他们的Android设备享受音频时,用户有很多选择。大多数设备都有内置扬声器,有线耳机的耳机插孔,许多设备还具有蓝牙连接和支持A2DP音频。

检查正在使用的硬件

您的应用程序的行为可能会受到输出路由到的硬件的影响。

您可以查询AudioManager以确定音频当前是否路由到设备扬声器,有线耳机或连接的蓝牙设备,如以下代码段所示:

if (isBluetoothA2dpOn()) {
    // Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
    // Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
    // Adjust output for headsets
} else {
    // If audio plays and noone can hear it, is it still playing?
}

处理音频输出硬件的更改

当耳机拔掉或蓝牙设备断开连接时,音频流自动重新路由到内置扬声器。如果你听到你的音乐高达我的音量,这可能是一个嘈杂的惊喜。

幸运的是,当发生这种情况时,系统广播ACTION_AUDIO_BECOMING_NOISY Intent。这是一个好的习惯,注册一个BroadcastReceiver监听这个Intent,无论你在播放音频。在音乐播放器的情况下,用户通常期望暂停播放,而对于游戏,可以选择显着降低音量。

private class NoisyAudioStreamReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
            // Pause the playback
        }
    }
}

private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);

private void startPlayback() {
    registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
}

private void stopPlayback() {
    unregisterReceiver(myNoisyAudioStreamReceiver);
}

猜你喜欢

转载自blog.csdn.net/jimo_lonely/article/details/53520402