SoundPool使用
SoundPool对象
新版本已经不建议使用默认构造方法去实例化SoundPool对象了;
/*
* @deprecated use {@link SoundPool.Builder} instead to create and configure a
* SoundPool instance
*/
public SoundPool(int maxStreams, int streamType, int srcQuality)
下面通过Builder模式构建SoundPool对象
SoundPool.Builder builder = new SoundPool.Builder();
//传入单次最多播放音频流数量,
builder.setMaxStreams(1);
//AudioAttributes封装音频属性的类
AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
//设置音频流的媒体类型,
//因为我后台有广告使用media播放,所以这类不用STREAM_MUSIC避免冲突
attrBuilder.setLegacyStreamType(AudioManager.STREAM_ALARM);
//设置AudioAttributes到构造器上
builder.setAudioAttributes(attrBuilder.build());
//构造出相应的对象
soundPool = builder.build();
加载音频
// priority 优先级,看注解应该是为将来兼容性考虑,直接设置1就行
//通过音频路径path加载
public int load(String path, int priority)
//通过资源raw加载
public int load(Context context, int resId, int priority)
//通过asset资源加载
public int load(AssetFileDescriptor afd, int priority)
//通过文件描述符加载,上述三个方法最终都是调用该方法进行具体实现
public int load(FileDescriptor fd, long offset, long length, int priority)
我这边音频比较多,还分国家的,所以我使用了asset,保持了目录结构。
//sb对象是用StringBuilder拼接的文件目录加文件名
AssetFileDescriptor assetFileDescriptor = activity.getResources().getAssets().openFd(sb.toString());
//此处获取到的voiceId 需要保存,后面播放的时候进行使用
int voiceId = soundPool.load(assetFileDescriptor, 1);
注意 加载完成后不能立刻进行播放,否则播放不出声音。
通过设置回调方法监听加载音频是否完成,来判断是否进行音频播报。
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
//加载音频完成
}
});
音频播报
只有一个播放函数,各个参数含义看注解
//第一个参数音频id,是前面load方法返回值
//第二个参数leftVolume为左声道音量值(范围= 0.0到1.0)
//第三个参数rightVolume为右声道音量值(范围= 0.0到1.0)
//第四个参数priority 为优先级
//第五个参数loop 为音频重复播放次数,0为值播放一次,-1为无限循环,其他值为播放loop+1次
//第六个参数 rate为播放的速率,范围0.5-2.0(0.5为一半速率,1.0为正常速率,2.0为两倍速率)
soundPool.play(voiceId, 1, 1, 1, 0, 1);
播报被覆盖问题
音频播报会有一个问题:频繁播放同一个voiceId的时候,上一次没播放完,下一次再播放会中断上次的播放再播放,要是太过频繁会导致音频反复被覆盖,播放体验不佳。
解决办法:
音频流都是通过AudioManager进行管理的,发现AudioManager中有isMusicActive可以判断AudioManager.STREAM_MUSIC是否在播放。
/**
* Checks whether any music is active.
*
* @return true if any music tracks are active.
*/
public boolean isMusicActive() {
return AudioSystem.isStreamActive(STREAM_MUSIC, 0);
}
由于我使用的不是AudioManager.STREAM_MUSIC,而是AudioManager.STREAM_ALARM。AudioManager中没有相关方法,所以我们可以直接通过AudioSystem.isStreamActive()方法去实现。而AudioSystem是一个hide注解类,isStreamActive是一个静态方法,所以我们需要使用反射。
private boolean isSoundPoolPlay() {
//通过反射判断STREAM_ALARM类型的音频流是否在执行
//目标方法:AudioSystem.isStreamActive(AudioManager.STREAM_ALARM, 0);
Class<?> threadClazz = null;
try {
threadClazz = Class.forName("android.media.AudioSystem");
Method method = threadClazz.getMethod("isStreamActive", int.class, int.class);
return (Boolean) method.invoke(null, AudioManager.STREAM_ALARM, 0);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
资源释放
最后再进行资源释放
if(soundPool!=null){
soundPool.autoPause();
for (Integer voiceId: soundPoolMap.values()) {
//将前面通过load获取到的voiceId进行unload
soundPool.unload(voiceId);
}
soundPool.release();
soundPool = null;
}