Android9 framework 按键音调用流程及自定义按键音(替换原生按键音)和调节按键音音量的方法

Android9 framework 按键音调用流程及自定义按键音(替换原生按键音)和调节按键音音量的方法

一、按键音调用流程

摘要:按键音播放的总体逻辑是先找到系统中按键音的资源,然后调用SoundPool.load让系统加载音频资源,加载成功后在onLoadComplete回调中会返回一个非0的soundID ,用于播放时指定特定的音频,最后在需要播放按键音的时候直接根据soundID播放

1.Android按键音接口

Android按键音只有两个常用接口,分别是:

原生设置APP中SoundFragment.java调用的设置按键音开关的接口:mAudioManager.loadSoundEffects()和mAudioManager.unloadSoundEffects()

private void setSoundEffectsEnabled(boolean enabled) {

mAudioManager = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); //1

if (enabled) {

mAudioManager.loadSoundEffects();

} else {

mAudioManager.unloadSoundEffects();

}

Settings.System.putInt(getActivity().getContentResolver(),

Settings.System.SOUND_EFFECTS_ENABLED, enabled ? 1 : 0);

}

先调用AudioManager的loadSoundEffects方法,然后会调用到AudioService的loadSoundEffects方法

View.java中播放按键音的接口:playSoundEffect

public boolean performClick() {

// We still need to call this method to handle the cases where performClick() was called

// externally, instead of through performClickInternal()

notifyAutofillManagerOnClick();

final boolean result;

final ListenerInfo li = mListenerInfo;

if (li != null && li.mOnClickListener != null) {

playSoundEffect(SoundEffectConstants.CLICK);//调用会经过ViewRootImpl.java,最终调用到AudioService中

li.mOnClickListener.onClick(this);

result = true;

} else {

result = false;

}

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

notifyEnterOrExitForAutoFillIfNeeded(true);

return result;

}

最终会调用到AudioService的playSoundEffect方法

2.onLoadSoundEffects()方法

上述的两个方法调用到AudioService之后,分别通过sendMsg向handler发送MSG_LOAD_SOUND_EFFECTS和MSG_PLAY_SOUND_EFFECT信息,handler在收到信息后会进行相应的操作,但是不管是哪个操作,都会调用到onLoadSoundEffects()方法

loadSoundEffects的调用流程(非重点):

public boolean loadSoundEffects() {

int attempts = 3;

LoadSoundEffectReply reply = new LoadSoundEffectReply();

synchronized (reply) {

//调用sendMsg方法

sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);

while ((reply.mStatus == 1) && (attempts-- > 0)) {

try {

reply.wait(SOUND_EFFECTS_LOAD_TIMEOUT_MS);

} catch (InterruptedException e) {

Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");

}

}

}

return (reply.mStatus == 0);

}

//sendMsg方法是对handler.sendMessageAtTime的封装

private static void sendMsg(Handler handler, int msg,

int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {

if (existingMsgPolicy == SENDMSG_REPLACE) {

handler.removeMessages(msg);

} else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {

return;

}

synchronized (mLastDeviceConnectMsgTime) {

long time = SystemClock.uptimeMillis() + delay;

if (msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||

msg == MSG_SET_A2DP_SINK_CONNECTION_STATE ||

msg == MSG_SET_HEARING_AID_CONNECTION_STATE ||

msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||

msg == MSG_A2DP_DEVICE_CONFIG_CHANGE ||

msg == MSG_BTA2DP_DOCK_TIMEOUT) {

if (mLastDeviceConnectMsgTime >= time) {

// add a little delay to make sure messages are ordered as expected

time = mLastDeviceConnectMsgTime + 30;

}

mLastDeviceConnectMsgTime = time;

}

handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);

}

}

//在handleMessage中处理消息

@Override

public void handleMessage(Message msg) {

......

case MSG_PLAY_SOUND_EFFECT:

//调用onPlaySoundEffect

onPlaySoundEffect(msg.arg1, msg.arg2);

break;

}

private void onPlaySoundEffect(int effectType, int volume) {

synchronized (mSoundEffectsLock) {

//最终会调用到onLoadSoundEffects

onLoadSoundEffects();

......

}

playSoundEffect的调用流程(非重点):

public void playSoundEffect(int effectType) {

playSoundEffectVolume(effectType, -1.0f);

}

public void playSoundEffectVolume(int effectType, float volume) {

// do not try to play the sound effect if the system stream is muted

if (isStreamMutedByRingerOrZenMode(STREAM_SYSTEM)) {

return;

}

if (effectType >= AudioManager.NUM_SOUND_EFFECTS || effectType < 0) {

Log.w(TAG, "AudioService effectType value " + effectType + " out of range");

return;

}

sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,

effectType, (int) (volume * 1000), null

相关创意

大桥未久
beat365投注网站

大桥未久

📅 07-10 👁️ 7523
阴阳师涂壁在哪刷 涂壁分布位置介绍一览
beat365手机中文官方网站

阴阳师涂壁在哪刷 涂壁分布位置介绍一览

📅 07-02 👁️ 4586
广的成语
beat365投注网站

广的成语

📅 07-09 👁️ 1716
联通卡月租最低多少 – 2025年最省钱的联通套餐实测
beat365手机中文官方网站

联通卡月租最低多少 – 2025年最省钱的联通套餐实测

📅 07-02 👁️ 2873
视频号小视频上传-怎样在搜狐视频上传工具软件
365bet平台总代

视频号小视频上传-怎样在搜狐视频上传工具软件

📅 07-11 👁️ 5000
一鍵修復照片馬賽克!最好用的6個照片去除馬賽克工具推薦
beat365手机中文官方网站

一鍵修復照片馬賽克!最好用的6個照片去除馬賽克工具推薦

📅 06-30 👁️ 524
夫妻生活中,为什么会进化出“高潮”的生理机制?看完你会很惊讶
红魔3电池容量多大 红魔3续航怎么样
beat365投注网站

红魔3电池容量多大 红魔3续航怎么样

📅 07-03 👁️ 3004