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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章