千家信息网

Android中如何进行绝对音量和相对音量设置

发表于:2025-11-12 作者:千家信息网编辑
千家信息网最后更新 2025年11月12日,这篇文章给大家介绍Android中如何进行绝对音量和相对音量设置,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。前言:绝对音量: 手机端不处理音量,只把当前音量告诉耳机,耳机端处理
千家信息网最后更新 2025年11月12日Android中如何进行绝对音量和相对音量设置

这篇文章给大家介绍Android中如何进行绝对音量和相对音量设置,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

前言:

绝对音量: 手机端不处理音量,只把当前音量告诉耳机,耳机端处理音量。

相对音量: 手机端处理,耳机端不确定是默认最大,还是有一个默认音量还是按照之前设置的绝对音量的值处理的。

手机音量转换绝对音量,此处会有一个计算过程,大致就是手机侧计算出当前音量和最大音量的百分比然后设置给耳机,耳机端收到百分比去处理。不然每个手机的音量范围不同,不做归一化就很难适配。

当通过音量按键调节音量时,会通过input事件分发,input把event分发给mediassesion, mediasession调用audiomanager的adjustStreamVolume调节音量。

当通过拖动音量条调节音量时,settings app会调用audiomanager的setStreamVolume调节音量。

adjustStreamVolume和setStreamVolume处理过程类似,接下来只看setStreamVolume。

setStreamVolume调用

// frameworks/base/media/java/android/media/AudioManager.javapublic void setStreamVolume(int streamType, int index, int flags) {    final IAudioService service = getService();    try {        service.setStreamVolume(streamType, index, flags, getContext().getOpPackageName()); // audiomanager调用的是audioservice的setStreamVolume    } catch (RemoteException e) {        throw e.rethrowFromSystemServer();    }}

audioservice的调用过程:

// frameworks/base/services/core/java/com/android/server/audio/AudioService.javaprivate void setStreamVolume(int streamType, int index, int flags, String callingPackage, String caller, int uid, boolean hasModifyAudioSettings) {    ensureValidStreamType(streamType);    int streamTypeAlias = mStreamVolumeAlias[streamType];    VolumeStreamState streamState = mStreamStates[streamTypeAlias];    inal int device = getDeviceForStream(streamType); // 获取streamType对应的device    int oldIndex;      synchronized (mSafeMediaVolumeStateLock) {        // reset any pending volume command        mPendingVolumeCommand = null;        oldIndex = streamState.getIndex(device);        android.media.AudioServiceInjector.mOriginalIndexWhenSetStreamVolume = index;        index = rescaleIndex(index * 10, streamType, streamTypeAlias);         // 设置绝对音量        if (streamTypeAlias == AudioSystem.STREAM_MUSIC && AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device) && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {            mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);        }         if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {            setSystemAudioVolume(oldIndex, index, getStreamMaxVolume(streamType), flags); // 这里是和hdmi相关,不用管        }         flags &= ~AudioManager.FLAG_FIXED_VOLUME;        if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {            flags |= AudioManager.FLAG_FIXED_VOLUME;             // volume is either 0 or max allowed for fixed volume devices            if (index != 0) {                if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && mSafeMediaVolumeDevices.contains(device)) {                   index = safeMediaVolumeIndex(device);                } else {                   index = streamState.getMaxIndex();                }            }        }         if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {            mVolumeController.postDisplaySafeVolumeWarning(flags); // 这里应该就是安全提醒,比如音量过大损伤听力的提醒            mPendingVolumeCommand = new StreamVolumeCommand(streamType, index, flags, device);        } else {            onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings); // 调用onSetStreamVolume设置音量            index = mStreamStates[streamType].getIndex(device);        }    }    sendVolumeUpdate(streamType, oldIndex, index, flags, device); }

onSetStreamVolume 和 sendVolumeUpdate的作用:

onSetStreamVolume -> 设置音量到底层并处理一些静音逻辑。

sendVolumeUpdate -> 通知ui更新音量值

这里插入个当音量设置到0时自动静音的处理:

// frameworks/base/services/core/java/com/android/server/audio/AudioService.javaprivate void onSetStreamVolume(int streamType, int index, int flags, int device, String caller, boolean hasModifyAudioSettings) {    final int stream = mStreamVolumeAlias[streamType];    setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings); // 设置音量到底层    // setting volume on ui sounds stream type also controls silent mode    if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || (stream == getUiSoundsStreamType())) {       int ringerMode = getNewRingerMode(stream, index, flags); // 获取ringmode        miuiRingerMode = miui.util.AudioManagerHelper.getValidatedRingerMode(mContext, miuiRingerMode); // 获取ringmode        setRingerMode(ringerMode, TAG + ".onSetStreamVolume", false /*external*/); // 设置ringmode    }}    private int getNewRingerMode(int stream, int index, int flags) {    if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || (stream == getUiSoundsStreamType())) {        int newRingerMode;        if (index == 0) { // 如果设置的音量是0        // 如果有震动就设置ringmode是震动模式,否则判断volumeDownToEnterSilent决定设置静音还是不静音            newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE : mVolumePolicy.volumeDownToEnterSilent ? AudioManager.RINGER_MODE_SILENT : AudioManager.RINGER_MODE_NORMAL;        } else {            newRingerMode = AudioManager.RINGER_MODE_NORMAL; // 如果音量不是0,ringmode设置为非静音        }        return newRingerMode;    }    return getRingerModeExternal();}

设置绝对音量: postSetAvrcpAbsoluteVolumeIndex

// frameworks/base/services/core/java/com/android/server/audio/AudioDeviceBroker.javavoid postSetAvrcpAbsoluteVolumeIndex(int index) {    sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);} // 收到消息MSG_I_SET_AVRCP_ABSOLUTE_VOLUME,调用mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); // frameworks/base/services/core/java/com/android/server/audio/BtHelper.javasynchronized void setAvrcpAbsoluteVolumeIndex(int index) {    mA2dp.setAvrcpAbsoluteVolume(index);}

蓝牙侧绝对音量处理:

// vendor/qcom/opensource/commonsys/packages/apps/Bluetooth/src/com/android/bluetooth/a2dp/A2dpService.javapublic void setAvrcpAbsoluteVolume(int volume) {    if (mFactory.getAvrcpTargetService() != null) {        mFactory.getAvrcpTargetService().sendVolumeChanged(volume);        return;    }    if(ApmConstIntf.getLeAudioEnabled()) {        VolumeManagerIntf mVolumeManager = VolumeManagerIntf.get();        mVolumeManager.setMediaAbsoluteVolume(volume);        return;    }    synchronized(mBtAvrcpLock) {        if (mAvrcp_ext != null) {           mAvrcp_ext.setAbsoluteVolume(volume);           return;        }        if (mAvrcp != null) {           mAvrcp.setAbsoluteVolume(volume);        }    }} // vendor/qcom/opensource/commonsys/bluetooth_ext/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.javapublic void setAbsoluteVolume(int volume) {    Message msg = mHandler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME, volume, 0);    mHandler.sendMessage(msg);} 收到消息MSG_SET_ABSOLUTE_VOLUME之后调用如下case MSG_SET_ABSOLUTE_VOLUME: {    int avrcpVolume = convertToAvrcpVolume(msg.arg1);    ......} // 计算avrcpVolumeprivate int convertToAvrcpVolume(int volume) {   if(mAudioStreamMax == 150) {       return (int) Math.round((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);   }   return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax);}

从这里可以看到,蓝牙侧传给耳机的音量其实就是百分比。

从相对音量切换到绝对音量的处理:

蓝牙APP有一个NotificationReceiver,当绝对音量的开关发生变化NotificationReceiver就会收到信息,然后做如下处理:

private class NotificationReceiver extends BroadcastReceiver {    public void onReceive(Context context, Intent intent) {        if(ABS_VOLUME_ACTION.equals(intent.getAction())) {            handleDeviceAbsVolume(mac, value);        }    }}// handleDeviceAbsVolume就会做对应的处理,把音量值发送给耳机侧。

此处每次切换的时候会有log打印,log mask: handleDeviceAbsVolume|setVolumeNative

切换之后播放声音就会调用checkAndSetVolume把音量值设置到最大,保证手机侧无音量处理。

从绝对音量切换到相对音量的处理:

和上面一样,蓝牙APP NotificationReceiver会收到绝对音量开关关闭的消息,然后把消息发给耳机端。然后还会调用audio侧设置stream的音量。

log mask: handleDeviceAbsVolume|setStreamVolume

切换之后播放声音就会调用checkAndSetVolume把音量值设置到当前实际的音量值。

Android是什么

Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。

关于Android中如何进行绝对音量和相对音量设置就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

音量 处理 手机 耳机 切换 静音 消息 蓝牙 调节 最大 就是 百分 百分比 过程 还是 内容 声音 更多 帮助 开放 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 人数的数据库类型与长度 我的世界服务器极限模式在哪关 蘑菇街数据库 .net 对象数组存数据库 贵阳dell服务器总代理 小学网络安全手抄报简单漂亮 服务器如何识别一台电脑账号 软件开发完了对方不付款 上海诚信网络技术开发哪个正规 团队插件软件开发 湖北专业软件开发学习 工程力学笔记软件开发 服务器管理控制器fpga 数据库中表单设计是什么 国际网络安全的挑战 怎样使服务器安全 天津市网络安全工作会议 和平精英服务器连接失败为什么 数据库技术人员任职要求 应用软件开发技术的基本框架外包 达梦数据库设置事务隔离级别 平谷区品牌软件开发专业服务 安徽数据库防伪技术 安徽调度服务器虚拟主机 大鹏在互联网科技公司实习 关于网络安全的应急计划 嘉兴网络安全准入控制供应商 服务器防护指南 软件开发预研 使命召唤ol连接服务器
0