摘要:前言當(dāng)我們點擊屏幕按鍵時,就會聽到音,那么音是如何播放起來的呢,由于最近項目需求順便熟悉下了音的邏輯。完成的繪制過程,包括過程。向分發(fā)收到的用戶發(fā)起的事件,如按鍵,觸屏等事件。總結(jié)音的流程就簡單分析到這里,歡迎大家交流指正。
前言
當(dāng)我們點擊屏幕按鍵時,就會聽到touch音,那么touch音是如何播放起來的呢,由于最近項目需求順便熟悉下了touch音的邏輯。
正文談touch邏輯首先要說下這個類ViewRootImpl.java,位于frameworks/base/core/java/android/view下,ViewRootImpl的主要功能:
A:鏈接WindowManager和DecorView的紐帶,更廣一點可以說是Window和View之間的紐帶。
B:完成View的繪制過程,包括measure、layout、draw過程。
C:向DecorView分發(fā)收到的用戶發(fā)起的event事件,如按鍵,觸屏等事件。
關(guān)于ViewRootImpl的源碼可參照博客ViewRootImpl類源碼解析,我們從performFocusNavigation()入手
private boolean performFocusNavigation(KeyEvent event) { //略 if (v.requestFocus(direction, mTempRect)) { playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction)); return true; } //略 return false; }
當(dāng)我們點擊某個控件時,會先觸發(fā)performFocusNavigation()這個方法,然后當(dāng)控件獲取到focus后便會調(diào)用playSoundEffect()方法,我只截取了performFocusNavigation()中關(guān)鍵代碼playSoundEffect()部分,來看下playSoundEffect()這個方法
public void playSoundEffect(int effectId) { checkThread(); try { final AudioManager audioManager = getAudioManager(); switch (effectId) { case SoundEffectConstants.CLICK: audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK); return; case SoundEffectConstants.NAVIGATION_DOWN: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN); return; case SoundEffectConstants.NAVIGATION_LEFT: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT); return; case SoundEffectConstants.NAVIGATION_RIGHT: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT); return; case SoundEffectConstants.NAVIGATION_UP: audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP); return; default: throw new IllegalArgumentException("unknown effect id " + effectId + " not defined in " + SoundEffectConstants.class.getCanonicalName()); } } catch (IllegalStateException e) { // Exception thrown by getAudioManager() when mView is null Log.e(mTag, "FATAL EXCEPTION when attempting to play sound effect: " + e); e.printStackTrace(); } }
發(fā)現(xiàn)調(diào)用了audioManager的playSoundEffect()方法,audiomanager就不說了,接觸android audio最先接觸的可能就是AudioManager了,音量控制,聲音焦點申請等。接著看
public void playSoundEffect(int effectType) { if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) { return; } //查詢是否開啟touch音,如果settings中關(guān)閉了,則直接返回 if (!querySoundEffectsEnabled(Process.myUserHandle().getIdentifier())) { return; } final IAudioService service = getService(); try { //調(diào)用到AudioService的playSoundEffect() service.playSoundEffect(effectType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
查詢touch音是否可播放,因為畢竟在android的setting中有個touch音的開關(guān),如果可播放則調(diào)用到AudioService的playSoundEffect()
public void playSoundEffect(int effectType) { playSoundEffectVolume(effectType, -1.0f); } public void playSoundEffectVolume(int effectType, float volume) { 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, 0); }
其實AudioService初始化的時候會創(chuàng)建一個子線HandlerThread,HandlerThread主要處理一些相對耗時的操作,這里將播放touch音的功能放在了這個子線程中去執(zhí)行,這樣避免了主線程的阻塞,其實大家在做mediaplayer播放時也建議放在子線程去播放,接下來看看handler里對消息的處理,關(guān)鍵代碼如下
case MSG_PLAY_SOUND_EFFECT: if (msg.obj == null) { onPlaySoundEffect(msg.arg1, msg.arg2, 0); } else { onPlaySoundEffect(msg.arg1, msg.arg2, (int) msg.obj); } break;
直接調(diào)用onPlaySoundEffect()的方法
private void onPlaySoundEffect(int effectType, int volume) { synchronized (mSoundEffectsLock) { //初始化mSoundPool和要播放的資源文件 onLoadSoundEffects(); if (mSoundPool == null) { return; } float volFloat; // use default if volume is not specified by caller if (volume < 0) { volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20); } else { volFloat = volume / 1000.0f; } if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { //播放touch音 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f); } else { MediaPlayer mediaPlayer = new MediaPlayer(); try { String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]); mediaPlayer.setDataSource(filePath); mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); mediaPlayer.prepare(); mediaPlayer.setVolume(volFloat); mediaPlayer.setOnCompletionListener(new OnCompletionListener() { public void onCompletion(MediaPlayer mp) { cleanupPlayer(mp); } }); mediaPlayer.setOnErrorListener(new OnErrorListener() { public boolean onError(MediaPlayer mp, int what, int extra) { cleanupPlayer(mp); return true; } }); mediaPlayer.start(); } catch (IOException ex) { Log.w(TAG, "MediaPlayer IOException: "+ex); } catch (IllegalArgumentException ex) { Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); } catch (IllegalStateException ex) { Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); } } } }
最終通過soundPool來播放指定的資源文件實現(xiàn)了touch音的播放,因此大家在工作中如果有什么需要對應(yīng)touch音的邏輯,可參照AudioService的onPlaySoundEffect()中的邏輯。
比如指定touch音的AudioAttributes使touch音輸出到指定的device上等。
touch音的流程就簡單分析到這里,歡迎大家交流指正。
努力學(xué)習(xí)ing~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/72669.html
摘要:在線程中,在中創(chuàng)建線程在線程中通過播放饒了一大圈竟然也用來播放就是從傳下來的需要注意,通知音是會申請焦點的??偨Y(jié)從創(chuàng)建到播放的流程基本就這樣,至于聲音的區(qū)分是否電話中,如果則使用播放,反之播放。 前言 我們在做Android開發(fā)的時候,免不了會使用到Notification,而且在android設(shè)備的設(shè)置中還可以設(shè)置通知音的優(yōu)先級,以及播放的聲音種類。那么通知音是如何播放的呢,今天我們...
閱讀 3032·2023-04-25 20:22
閱讀 3346·2019-08-30 11:14
閱讀 2599·2019-08-29 13:03
閱讀 3187·2019-08-26 13:47
閱讀 3230·2019-08-26 10:22
閱讀 1276·2019-08-23 18:26
閱讀 623·2019-08-23 17:16
閱讀 1920·2019-08-23 17:01