Android AudioManager

Android AudioManager

API

AudioManager(audio翻译过来就是声音、音频):

AudioManager,音频管理类,它主要提供了丰富的API让开发者对应用的音量和铃声模式进行控制以及访问。主要内容涉及到音频流、声音、蓝牙、扩音器、耳机等等。AudioManager类位于android.Media 包中。

A:获取实例

​ 由于音频管理涉及到多媒体,因此这个AudioManager获取实例的方式是这样的:

AudioManager audio = (AudioManager)Context.getSystemService(Context.AUDIO_SERVICE);

B:丰富的API

音频管理类提供了大量的API,这些API是我们经常看到或者用到的,比如,调节音量,我相信对于很多人来说,调节音量这个姿势是很常见的,比如你打开某视频APP、某音乐APP其中肯定有调节音量大小的手势,那么调节音量内部的逻辑可以使用 adjustStreamVolume(int streamType, int direction, int flags)

参数预览:

streamType :要调整的音频流类型。类型有以下几种:

STREAM_VOICE_CALL(电话的音频流),

STREAM_SYSTEM(系统声音的音频流),

STREAM_RING(电话铃声的音频流),

STREAM_MUSIC(用于音乐播放的音频流)

STREAM_ALARM(警报的音频流)

STREAM_ACCESSIBILITY(无障碍的音频流)

STREAM_NOTIFICATION(通知的音频流)

区分流类型的目的是让用户能够单独地控制不同的种类的音频。但上述音频种类中,大多数都是被系统限制。除非应用需要做替换闹钟的铃声的操作,不然的话你只能通过STREAM_MUSIC来播放你的音频。也就是说我们最常见操作的就是STREAM_MUSIC这个类型。

direction :调整音量的方向。其中: ADJUST_LOWER(减少铃声音量),ADJUST_RAISE(增加铃声音量)或 ADJUST_SAME(保持之前的铃声音量)

flags :一个或多个标志。可能这里的标志不是很好理解,是这样,AudioManager提供了一些常量,我们可以将这些系统已经准备好的常量设置为这里的flags,比如:

FLAG_ALLOW_RINGER_MODES(更改音量时是否包括振铃模式作为可能的选项),

FLAG_PLAY_SOUND(是否在改变音量时播放声音),

FLAG_REMOVE_SOUND_AND_VIBRATE(删除可能在队列中或正在播放的任何声音/振动(与更改音量有关)),

FLAG_SHOW_UI(显示包含当前音量的吐司),

FLAG_VIBRATE(是否进入振动振铃模式时是否振动)

比如我现在想要增加音量,就可以这样写:

AudioManager . adjustStreamVolume (AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI);

这句代码的意思是指:指定调节类型为 音乐的音频,增大音量,显示音量图形示意。举一反三下面就是降低音量的代码:

AudioManager . adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_PLAY_SOUND);

比如,我想要获取手机的音量,可以调取getStreamVolume(int streamType); 这里的streamType指获得手机的当前流类型的音量,最大值为15(不同手机可能不同)最小值为0。

setStreamVolume(int streamType, int index,int flags)这个API顾名思义就是根据音频流类型去设置音量大小的。主要这里的index不能超过最大索引,也就是15。

getMode()

返回当前音频模式,如 NORMAL(普通), RINGTONE(铃声), orIN_CALL(通话)

setMode()

设置声音模式,可取值NORMAL(普通), RINGTONE(铃声), or IN_CALL(通话)

getRingerMode()

返回当前的铃声模式。如RINGER_MODE_NORMAL(普通)、RINGER_MODE_SILENT(静音)、RINGER_MODE_VIBRATE(震动)

setRingerMode(int ringerMode)

改变铃声模式

getStreamVolume(int streamType)

取得当前手机的音量,最大值为15,最小值为0,当为0时,手机自动将模式调整为"震动模式"。

getStreamMaxVolume(int streamType)

获得当前手机最大铃声。

setStreamMute(int streamType, boolean state) 静音或不静音音频流 设置指定声音类型(streamType)是否为静音。如果state为true,则设置为静音;否则,不设置为静音。

麦克风相关

setMicrophoneMute(boolean on):设置麦克风静音开启或关闭。( 设置true 关闭麦克风也就是麦克风静音; 设置false,即关闭静音打开麦克风)

setSpeakerphoneOn(boolean on):这个方法主要是判断是否打开扩音器(设置true,即打开免提电话; false将其关闭)

isMicrophoneMute():判断麦克风是否静音或是否打开(如果麦克风静音则为true,否则为false)

isMusicActive():判断是否有音乐处于活跃状态(如果任何音乐曲目有效,则为true)

比如蓝牙相关的API

isBluetoothA2dpOn() :这个方法是检查是否打开或关闭了到蓝牙耳机的A2DP音频路由。

isBluetoothScoOn(): 这个方法主要是检查通信是否使用蓝牙SCO。

startBluetoothSco(): 启动蓝牙SCO音频连接

setBluetoothScoOn(boolean on): 请求使用蓝牙SCO耳机进行通信。(设置true 代表用于通信的蓝牙SCO; 设置false 即不使用蓝牙SCO进行通信)

void stopBluetoothSco(): 停止蓝牙SCO音频连接。

等等,具体的更丰富更全面的API可以参考 AudioManager官方文档

代码

获取AudioManager实例

要使用AudioManager类,首先需要获取一个AudioManager实例。可以通过以下代码来获取:

复制代码
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

音量控制

通过AudioManager类,我们可以控制设备的各种音量,包括媒体音量、闹钟音量、通话音量等等。下面是一些常用的方法:

获取当前音量

可以使用getStreamVolume()方法来获取当前音量。下面的代码演示了如何获取媒体音量:

复制代码
int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
设置音量

可以使用setStreamVolume()方法来设置音量。下面的代码演示了如何将媒体音量设置为最大:

复制代码
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume, 0);
调整音量

如果希望逐步调整音量,可以使用adjustStreamVolume()方法。下面的代码演示了如何将媒体音量调低一个单位:

复制代码
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0);

铃声模式控制

除了音量控制外,AudioManager还允许我们管理设备的铃声模式。以下是一些常用的方法:

获取当前铃声模式

使用getRingerMode()方法可以获取当前的铃声模式。返回值可以是以下之一:RINGER_MODE_NORMAL、RINGER_MODE_SILENT、RINGER_MODE_VIBRATE。

复制代码
int ringerMode = audioManager.getRingerMode();
设置铃声模式

可以使用setRingerMode()方法来设置铃声模式。以下是一些常用的设置:

  1. RINGER_MODE_NORMAL:设置为正常模式,即响铃和震动都开启。

  2. RINGER_MODE_SILENT:设置为静音模式,即不响铃也不震动。

  3. RINGER_MODE_VIBRATE:设置为震动模式,即不响铃但会震动。

    audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);

监听音频焦点

在开发中,有时候需要监听音频焦点的变化,比如当其他应用开始播放音频时暂停当前应用的音乐。通过AudioManager类,我们可以实现这样的功能。

请求音频焦点

使用requestAudioFocus()方法可以请求音频焦点,并指定焦点类型、音频焦点变化的回调等参数。下面的代码演示了如何请求音频焦点:

复制代码
AudioManager.OnAudioFocusChangeListener focusChangeListener = new AudioManager.OnAudioFocusChangeListener(){
    @Override
    public void onAudioFocusChange(int focusChange) {
        // 处理音频焦点变化事件
    }
};

int result = audioManager.requestAudioFocus(focusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
释放音频焦点

当不再需要音频焦点时,可以调用abandonAudioFocus()方法来释放焦点。

复制代码
audioManager.abandonAudioFocus(focusChangeListener);

监听系统音量控制按键

除了我们手动去改之外,用户也可以通过物理按键或是耳机来控制音量,这时,我们理应也要做出相应的改变,所以,还需要对音量按键做监听才行。

这里就用到熟悉的老方法了,重写ActivityonKeyDown方法:

javascript 复制代码
    /**
     * 监听并接管系统的音量按键,
     * 注意:最好保持原有逻辑不变
     */
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        when (keyCode) {
            //音量+按键
            KeyEvent.KEYCODE_VOLUME_UP -> {
                
                return true
            }
            //音量-按键
            KeyEvent.KEYCODE_VOLUME_DOWN -> {
                
                return true
            }
        }
        return super.onKeyDown(keyCode, event)
    }

音频焦点

介绍

两个或两个以上的 Android App可同时向同一输出流(比如手机的蓝牙、手机的喇叭)播放音频,系统会将所有 音频流(就是音频数据了)混合在一起。这是一项有意思的技术,但却会出现混音。为了避免所有音乐应用同时播放,Android 引入了 "音频焦点"的概念。 音频焦点机制是Android系统提供的一种道德约定 ,它倡导的东西有三点:

1、 只有一个App持有音频焦点;

2 、播放声音前申请音频焦点,不需要播放的时候释放音频焦点;

3 、失去音频焦点应该暂停播放或者降低音量。
音频焦点是Android系统进程管理的一个值,这个值就记录了当前音频焦点属于哪个应用,类型等。

使用中注意的点:

① 为 requestAudioFocus 方法传入的第3个参数:

  • 如果计划在将来一段时间内播放音频,并且希望前一个持有音频焦点的应用停止播放,则应该请求永久性的音频焦点 (AUDIOFOCUS_GAIN)。
  • 如果只希望在短时间内播放音频,并且希望前一个持有音频焦点的应用暂停播放,则应该请求暂时性的焦点 (AUDIOFOCUS_GAIN_TRANSIENT)。
  • 如果只希望在短时间内播放音频,并允许前一个持有焦点的应用在降低音量的情况下继续播放,则应该请求"降低音量"的暂时性焦点(AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) 。这两个音频会混合到音频流中同时输出。

② AudioManager.OnAudioFocusChangeListener回调回来的音频焦点类型,如下:

  • AUDIOFOCUS_GAIN:获取得到音频焦点。
  • AUDIOFOCUS_LOSS:永久性失去音频焦点,后续不会再收到 AUDIOFOCUS_GAIN 回调。应用应立即暂停播放,此时其他应用会播放音频。
  • AUDIOFOCUS_LOSS_TRANSIENT:暂时性失去音频焦点,应用应暂停播放。当抢占焦点的应用放弃焦点时、自己的应用可以收到 AUDIOFOCUS_GAIN 回调。
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时性失去音频焦点,应用应降低音量。当抢占焦点的应用放弃焦点时、自己的应用可以收到 AUDIOFOCUS_GAIN 回调。

实现音频焦点的步骤

要实现音频焦点,可以遵循以下步骤:

  • 请求音频焦点

  • 维护音频焦点变更:

    1. AUDIOFOCUS_GAIN:应用程序已经拥有了焦点,可以播放音频并且恢复至最近一次播放的状态。
    2. AUDIOFOCUS_LOSS:失去了焦点,需要停止播放并清理音频资源。
    3. AUDIOFOCUS_LOSS_TRANSIENT:失去焦点,但很快就能返回,并且和AUDIOFOCUS_LOSS的关键区别是不需要释放音频资源,因为等待时间很短。
    4. AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:失去焦点,但可以在音量稍稍降低后继续播放音频。
  • 释放音频焦点

代码

复制代码
public class MediaPlayerDemo extends Activity implements
        OnAudioFocusChangeListener {
    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    MediaPlayer mediaPlayer;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    private boolean requestAudioFocus() {
        int result = audioManager.requestAudioFocus(this,
                AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            return true;
        }
        return false;
    }

    private void releaseAudioFocus() {
        audioManager.abandonAudioFocus(this);
    }

    @Override
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // 恢复音频播放
                if (mediaPlayer == null) {
                    initMediaPlayer();
                }
                else if (!mediaPlayer.isPlaying()) {
                    mediaPlayer.start();
                }
                mediaPlayer.setVolume(1.0f, 1.0f);
                break;

            case AudioManager.AUDIOFOCUS_LOSS:
                // 失去音频焦点退出程序
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.stop();
                }
                mediaPlayer.release();
                mediaPlayer = null;
                break;

            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // 暂时失去焦点,暂停播放等待重新获得音频焦点
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                }
                break;

            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                // 暂时失去焦点,音量减小,等待重新获得音频焦点
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.setVolume(0.1f, 0.1f);
                }
                break;
        }
    }
}

其中,requestAudioFocus()方法请求音频焦点,releaseAudioFocus()方法释放音频焦点。onAudioFocusChange()方法响应音频焦点的状态变化。

焦点类型AUDIOFOCUS_GAIN_TRANSIENT表示当一段时间内需要持有音频焦点。例如,一些短暂的提示音效在它们播放期间重新获取音频焦点会更好。相对于AUDIOFOCUS_GAIN,AUDIOFOCUS_GAIN_TRANSIENT可以更快地响应其他应用程序的请求。

焦点类型AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK表示音频源可能在持有焦点播放时更小地响应。例如,当正在播放音乐的应用程序收到来自其他应用程序的通知时,音量将自动减小。

注意

大多数情况下,按照上述流程使用音频焦点是没有问题的。但线上有部分用户反馈无法使用语音唤醒功能(即无法通过语音来和应用交互),经过排查,发现有如下可优化的地方:

  • 问题1:不是所有应用都会将音频焦点释放,有的应用也会不释放,比如手机系统的录音机。这看似违背最上面说的音频焦点管理规则,但录音机是有它特殊性的,不管它位于前台还是后台,都应该拥有录音的功能。这就会导致只要不杀死录音机应用进程,它就会一直占用音频焦点,导致其他应用无法使用音频焦点。或者是其他应用把原本属于本应用的音频焦点抢占了,也会导致本应用无法使用音频焦点,比如某些应用内的语音通话功能就会抢占音频焦点。

    处理策略:当AudioManager.OnAudioFocusChangeListener监听器监听到音频焦点已失去时(AUDIOFOCUS_LOSS、AUDIOFOCUS_LOSS_TRANSIENT),就主动通过弹窗或toast提示用户【关闭其他应用或重启手机】,以期望达到让其他应用释放音频焦点的目的。

  • 问题2:正常来说,当B应用把音频焦点释放后,A应用是可以重新获取到音频焦点的回调(AUDIOFOCUS_GAIN),但某些Android手机可能会有系统bug,即不会主动通知A应用音频焦点已经回来了,A应用无法拿到AUDIOFOCUS_GAIN这个回调,A应用也就无法再恢复播放音频。

    处理策略:增加音频功能的调用时机。比如在切页或者做其他操作的时候,主动尝试使用语音唤醒的音频功能,而不是通过AudioManager.OnAudioFocusChangeListener监听器被动地等待回调。当主动尝试使用语音唤醒成功时,说明音频焦点已经回来了。

26版本之上

请求焦点

复制代码
mAudioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
						.setOnAudioFocusChangeListener(focusChangeListener)
                        .setAudioAttributes(new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build())
                        .setFocusGain(AudioManager.AUDIOFOCUS_GAIN)
                        .setForceDucking(true)
                        .setWillPauseWhenDucked(true)
		//可以设置其他的参数及listener
        .build();
mAudioManager.requestAudioFocus(mAudioFocusRequest);

放弃焦点

复制代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest);
}

参考

android audiomanager

Android音频管理之AudioManager

Android 修改系统音量及监听

音频焦点

音频焦点九问

提高应用交互体验:Android音频焦点如何调整

浅析Android音频焦点

相关推荐
还鮟4 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡5 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi005 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil7 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你7 小时前
Android View的绘制原理详解
android
移动开发者1号10 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号10 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best15 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk15 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭20 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin