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音频焦点

相关推荐
JasonYin~1 小时前
HarmonyOS NEXT 实战之元服务:静态案例效果---手机查看电量
android·华为·harmonyos
zhangphil1 小时前
Android adb查看某个进程的总线程数
android·adb
抛空2 小时前
Android14 - SystemServer进程的启动与工作流程分析
android
Gerry_Liang4 小时前
记一次 Android 高内存排查
android·性能优化·内存泄露·mat
天天打码5 小时前
ThinkPHP项目如何关闭runtime下Log日志文件记录
android·java·javascript
爱数学的程序猿8 小时前
Python入门:6.深入解析Python中的序列
android·服务器·python
brhhh_sehe8 小时前
重生之我在异世界学编程之C语言:深入文件操作篇(下)
android·c语言·网络
zhangphil8 小时前
Android基于Path的addRoundRect,Canvas剪切clipPath简洁的圆形图实现,Kotlin(2)
android·kotlin
Calvin8808288 小时前
Android Studio 的革命性更新:Project Quartz 和 Gemini,开启 AI 开发新时代!
android·人工智能·android studio
敲代码敲到头发茂密10 小时前
【大语言模型】LangChain 核心模块介绍(Memorys)
android·语言模型·langchain