xml文件地址:frameworks/base/core/res/res/xml/audio_assets.xml
音效文件地址:frameworks/base/data/sounds/effects/ogg
AudioManager的playSoundEffect用于播放Beep音,代码如下:
//frameworks/base/media/java/android/media/AudioManager.java
public class AudioManager {
public void playSoundEffect(@SystemSoundEffect int effectType) {
playSoundEffect(effectType, UserHandle.USER_CURRENT);
}
}
调用playSoundEffect的重载方法:
//frameworks/base/media/java/android/media/AudioManager.java
public class AudioManager {
public void playSoundEffect(@SystemSoundEffect int effectType, int userId) {
if (effectType < 0 || effectType >= NUM_SOUND_EFFECTS) {
return;
}
final IAudioService service = getService();
try {
service.playSoundEffect(effectType, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
调用IAudioService的playSoundEffect方法,IAudioService是一个接口,由AudioService实现:
//frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener,
AudioSystemAdapter.OnRoutingUpdatedListener,
AudioSystemAdapter.OnVolRangeInitRequestListener {
public void playSoundEffect(int effectType, int userId) {
if (querySoundEffectsEnabled(userId)) {
playSoundEffectVolume(effectType, -1.0f);
}
}
}
调用playSoundEffectVolume方法:
//frameworks/base/services/core/java/com/android/server/audio/AudioService.java
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener,
AudioSystemAdapter.OnRoutingUpdatedListener,
AudioSystemAdapter.OnVolRangeInitRequestListener {
private AudioHandler mAudioHandler;
public void playSoundEffectVolume(int effectType, float volume) {
// do not try to play the sound effect if the system stream is muted
if (isStreamMute(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, 0);
}
}
发送MSG_PLAY_SOUND_EFFECT消息,发送的消息在AudioHandler的handleMessage中处理:
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
AccessibilityManager.AccessibilityServicesStateChangeListener,
AudioSystemAdapter.OnRoutingUpdatedListener,
AudioSystemAdapter.OnVolRangeInitRequestListener {
private SoundEffectsHelper mSfxHelper;
/** Handles internal volume messages in separate volume thread. */
private class AudioHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PLAY_SOUND_EFFECT:
mSfxHelper.playSoundEffect(msg.arg1, msg.arg2);
break;
}
}
}
调用SoundEffectsHelper的playSoundEffect方法
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
/*package*/ void playSoundEffect(int effect, int volume) {
sendMsg(MSG_PLAY_EFFECT, effect, volume, null, 0);
}
}
发送MSG_PLAY_EFFECT消息:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private class SfxHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PLAY_EFFECT:
final int effect = msg.arg1, volume = msg.arg2;
onLoadSoundEffects(new OnEffectsLoadCompleteHandler() {
@Override
public void run(boolean success) {
if (success) {
onPlaySoundEffect(effect, volume);
}
}
});
break;
}
}
}
}
SoundEffectsHelper onLoadSoundEffects
调用onLoadSoundEffects方法:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private SoundPool mSoundPool;
private void onLoadSoundEffects(OnEffectsLoadCompleteHandler onComplete) {
if (mSoundPoolLoader != null) {
// Loading is ongoing.
mSoundPoolLoader.addHandler(onComplete);
return;
}
if (mSoundPool != null) {
if (onComplete != null) {
onComplete.run(true /*success*/);
}
return;
}
logEvent("effects loading started");
mSoundPool = new SoundPool.Builder()
.setMaxStreams(NUM_SOUNDPOOL_CHANNELS)
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build())
.build(); //创建SoundPool对象
loadSoundAssets(); //加载声音资料
mSoundPoolLoader = new SoundPoolLoader(); //创建SoundPoolLoader对象
mSoundPoolLoader.addHandler(new OnEffectsLoadCompleteHandler() { //增加OnEffectsLoadComplete Handler
@Override
public void run(boolean success) {
mSoundPoolLoader = null;
if (!success) {
Log.w(TAG, "onLoadSoundEffects(), Error while loading samples");
onUnloadSoundEffects();
}
}
});
mSoundPoolLoader.addHandler(onComplete); //增加onComplete Handler
int resourcesToLoad = 0;
for (Resource res : mResources) {
String filePath = getResourceFilePath(res);
int sampleId = mSoundPool.load(filePath, 0);
if (sampleId > 0) {
res.mSampleId = sampleId;
res.mLoaded = false;
resourcesToLoad++;
} else {
logEvent("effect " + filePath + " rejected by SoundPool");
Log.w(TAG, "SoundPool could not load file: " + filePath);
}
}
if (resourcesToLoad > 0) {
sendMsg(MSG_LOAD_EFFECTS_TIMEOUT, 0, 0, null, SOUND_EFFECTS_LOAD_TIMEOUT_MS);
} else {
logEvent("effects loading completed, no effects to load");
mSoundPoolLoader.onComplete(true /*success*/);
}
}
}
上面方法主要处理如下:
1、创建SoundPool对象
2、调用loadSoundAssets方法,建立音效文件的索引
3、创建SoundPoolLoader对象
4、调用SoundPoolLoader的addHandler方法
5、调用SoundPool的load方法
6、调用SoundPoolLoader的onComplete方法
下面分别进行分析:
new SoundPool
待更新
loadSoundAssets
调用loadSoundAssets方法,加载声音资料:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private final int[] mEffects = new int[AudioManager.NUM_SOUND_EFFECTS]; // indexes in mResources
private void loadSoundAssets() {
XmlResourceParser parser = null;
// only load assets once.
if (!mResources.isEmpty()) {
return;
}
loadSoundAssetDefaults(); //加载默认音效文件
try {
parser = mContext.getResources().getXml(com.android.internal.R.xml.audio_assets); //获取Xml,xml文件地址:frameworks/base/core/res/res/xml/audio_assets.xml
XmlUtils.beginDocument(parser, TAG_AUDIO_ASSETS);
String version = parser.getAttributeValue(null, ATTR_VERSION);
Map<Integer, Integer> parserCounter = new HashMap<>();
if (ASSET_FILE_VERSION.equals(version)) {
while (true) {
XmlUtils.nextElement(parser);
String element = parser.getName();
if (element == null) {
break;
}
if (element.equals(TAG_GROUP)) {
String name = parser.getAttributeValue(null, ATTR_GROUP_NAME);
if (!GROUP_TOUCH_SOUNDS.equals(name)) {
Log.w(TAG, "Unsupported group name: " + name);
}
} else if (element.equals(TAG_ASSET)) {
String id = parser.getAttributeValue(null, ATTR_ASSET_ID);
String file = parser.getAttributeValue(null, ATTR_ASSET_FILE);
int fx;
try {
Field field = AudioManager.class.getField(id);
fx = field.getInt(null);
} catch (Exception e) {
Log.w(TAG, "Invalid sound ID: " + id);
continue;
}
int currentParserCount = parserCounter.getOrDefault(fx, 0) + 1;
parserCounter.put(fx, currentParserCount);
if (currentParserCount > 1) {
Log.w(TAG, "Duplicate definition for sound ID: " + id);
}
mEffects[fx] = findOrAddResourceByFileName(file); //建立音效文件的索引
} else {
break;
}
}
boolean navigationRepeatFxParsed = allNavigationRepeatSoundsParsed(parserCounter);
boolean homeSoundParsed = parserCounter.getOrDefault(AudioManager.FX_HOME, 0) > 0;
if (navigationRepeatFxParsed || homeSoundParsed) {
AudioManager audioManager = mContext.getSystemService(AudioManager.class); //获取AudioManager对象
if (audioManager != null && navigationRepeatFxParsed) {
audioManager.setNavigationRepeatSoundEffectsEnabled(true); //调用audioManager的setNavigationRepeatSoundEffectsEnabled方法
}
if (audioManager != null && homeSoundParsed) {
audioManager.setHomeSoundEffectEnabled(true); //调用audioManager的setHomeSoundEffectEnabled方法
}
}
}
} catch (Resources.NotFoundException e) {
Log.w(TAG, "audio assets file not found", e);
} catch (XmlPullParserException e) {
Log.w(TAG, "XML parser exception reading sound assets", e);
} catch (IOException e) {
Log.w(TAG, "I/O exception reading sound assets", e);
} finally {
if (parser != null) {
parser.close();
}
}
}
}
上面方法主要处理如下:
1、调用loadSoundAssetDefaults()方法,加载默认音效文件
2、查询xml,建立音效文件的索引mEffects
下面分别进行分析:
loadSoundAssetDefaults
调用loadSoundAssetDefaults()方法,加载默认音效文件:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private final int[] mEffects = new int[AudioManager.NUM_SOUND_EFFECTS]; // indexes in mResources
private void loadSoundAssetDefaults() {
int defaultResourceIdx = mResources.size();
mResources.add(new Resource("Effect_Tick.ogg"));
Arrays.fill(mEffects, defaultResourceIdx);
}
}
new SoundPoolLoader
创建SoundPoolLoader对象,SoundPoolLoader为SoundEffectsHelper的内部类,构造方法如下:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private class SoundPoolLoader implements
android.media.SoundPool.OnLoadCompleteListener {
private List<OnEffectsLoadCompleteHandler> mLoadCompleteHandlers =
new ArrayList<OnEffectsLoadCompleteHandler>();
SoundPoolLoader() {
// SoundPool use the current Looper when creating its message handler.
// Since SoundPoolLoader is created on the SfxWorker thread, SoundPool's
// message handler ends up running on it (it's OK to have multiple
// handlers on the same Looper). Thus, onLoadComplete gets executed
// on the worker thread.
mSoundPool.setOnLoadCompleteListener(this); //设置OnLoadCompleteListener
}
}
}
SoundPoolLoader addHandler
调用SoundPoolLoader的addHandler方法,增加OnEffectsLoadCompleteHandler和onComplete。
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private class SoundPoolLoader implements
android.media.SoundPool.OnLoadCompleteListener {
void addHandler(OnEffectsLoadCompleteHandler handler) {
if (handler != null) {
mLoadCompleteHandlers.add(handler);
}
}
}
}
SoundPool load
调用SoundPool的load方法:
待更新
SoundPoolLoader onComplete
调用SoundPoolLoader的onComplete方法:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private SoundPool mSoundPool;
private class SoundPoolLoader implements
android.media.SoundPool.OnLoadCompleteListener {
void onComplete(boolean success) {
if (mSoundPool != null) {
mSoundPool.setOnLoadCompleteListener(null);
}
for (OnEffectsLoadCompleteHandler handler : mLoadCompleteHandlers) {
handler.run(success);
}
logEvent("effects loading " + (success ? "completed" : "failed"));
}
}
}
上面方法主要处理如下:
1、调用SoundPool的setOnLoadCompleteListener方法
2、调用OnEffectsLoadCompleteHandler的run方法,也就是在onLoadSoundEffects方法中调用SoundPoolLoader的addHandler注册的方法
下面分别进行分析:
SoundPool setOnLoadCompleteListener
调用SoundPool的setOnLoadCompleteListener方法:
//frameworks/base/media/java/android/media/SoundPool.java
public class SoundPool extends PlayerBase {
private final AtomicReference<EventHandler> mEventHandler = new AtomicReference<>(null);
public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
if (listener == null) {
mEventHandler.set(null);
return;
}
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler.set(new EventHandler(looper, listener));
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler.set(new EventHandler(looper, listener));
} else {
mEventHandler.set(null);
}
}
}
OnEffectsLoadCompleteHandler run
调用OnEffectsLoadCompleteHandler的run方法,也就是在onLoadSoundEffects方法中调用SoundPoolLoader的addHandler注册的方法,第一个注册的方法:
mSoundPoolLoader.addHandler(new OnEffectsLoadCompleteHandler() {
@Override
public void run(boolean success) {
mSoundPoolLoader = null;
if (!success) {
Log.w(TAG, "onLoadSoundEffects(), Error while loading samples");
onUnloadSoundEffects();
}
}
});
SoundEffectsHelper onUnloadSoundEffects
调用onUnloadSoundEffects方法:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
void onUnloadSoundEffects() {
if (mSoundPool == null) {
return;
}
if (mSoundPoolLoader != null) {
mSoundPoolLoader.addHandler(new OnEffectsLoadCompleteHandler() {
@Override
public void run(boolean success) {
onUnloadSoundEffects();
}
});
}
logEvent("effects unloading started");
for (Resource res : mResources) {
if (res.mSampleId != EFFECT_NOT_IN_SOUND_POOL) {
mSoundPool.unload(res.mSampleId); //调用SoundPool的unload方法
res.unload();
}
}
mSoundPool.release(); //调用SoundPool的release方法
mSoundPool = null;
logEvent("effects unloading completed");
}
}
第二个注册的方法:
onLoadSoundEffects(new OnEffectsLoadCompleteHandler() {
@Override
public void run(boolean success) {
if (success) {
onPlaySoundEffect(effect, volume);
}
}
});
SoundEffectsHelper onPlaySoundEffect
调用SoundEffectsHelper的onPlaySoundEffect方法:
//frameworks/base/service/java/com/android/server/audio/SoundEffectsHelper.java
class SoundEffectsHelper {
private SoundPool mSoundPool;
void onPlaySoundEffect(int effect, int volume) {
float volFloat;
// use default if volume is not specified by caller
if (volume < 0) {
volFloat = (float) Math.pow(10, (float) mSfxAttenuationDb / 20);
} else {
volFloat = volume / 1000.0f;
}
Resource res = mResources.get(mEffects[effect]); //通过索获取音效资源
if (mSoundPool != null && res.mSampleId != EFFECT_NOT_IN_SOUND_POOL && res.mLoaded) { //如果资源已经加载,则调用SoundPool的play进行播放
mSoundPool.play(res.mSampleId, volFloat, volFloat, 0, 0, 1.0f);
} else { //否则使用MediaPlayer 进行播放
MediaPlayer mediaPlayer = new MediaPlayer();
try {
String filePath = getResourceFilePath(res);
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);
}
}
}
}
上面方法主要处理如下:
1、调用SoundPool的play方法。
2、创建MediaPlay。
3、调用MediaPlayer的prepare方法。
4、调用MediaPlayer的start方法。
下面分别进行分析:
SoundPool play
待更新
new MediaPlay
待更新
MediaPlayer prepare
待更新
MediaPlayer start
待更新