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;
}