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

一、按键音调用流程

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

1.Android按键音接口

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

  1. 原生设置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方法

  1. 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
  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值