MediaPlayer
存在如下缺点:
- 资源占用量较高,延迟时间较长。
- 不支持多个音频同时播放。
除使用 MediaPlayer
播放音频之外,Android 还提供了 SoundPool
来播放音效,SoundPool
使用音效池的概念来管理多个短促的音效,例如它可以开始就加载20个音效,以后在程序中按音效的 ID 进行播放。
SoundPool
主要用于播放一些较短的声音片段,与 MediaPlayer
相比,SoundPool
的优势在于 CPU 资源占用量低和反应延迟小。另外,SoundPool
还支持自行设置声音的品质、音量、播放比率等参数。
Android 系统 SoundPool
提供了一个 Builder
内部类,该内部类专门用于创建 SoundPool
。
一旦得到了 SoundPool
对象之后,接下来就可调用 SoundPool
的多个重裁的 load()
方法来加载声音了。SoundPool
提供了如下 4 个 Load()
方法。
int load(Context context, int resId, int priority)
从 resId
所对应的资源加载声音。
int load(FileDescriptor fd, long offset, long length, int priority)
加载 fd
所对应的文件中从 offset
开始、长度为 length
的声音。
int load(AssetFileDescriptor afd, int priority)
从 afd
所对应的文件中加载声音。
int load(String path, int priority)
从 path
对应的文件去加载声音。
上面 4 个方法中都有一个 priority
参数,该参数目前还没有任何作用,Andtroid 建议将该参数设为 1,保持和未来的兼容性。
上面 4 个方法加载声音之后,都会返回该声音的 ID,以后程序就可以通过该声音的 ID 来播放指定声音了:
int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
该方法的第一个参数指定播放哪个声音;leftVolume
、rightVolume
指定左、右的音量;priority
指定播放声音的优先级,数值越大,优先级越高;loop
指定是否循环,0 为不循环,-1 为循环;rate
指定播放的比率,数值可从 0.5 到 2,1 为正常比率。
public class MainActivity extends AppCompatActivity {
// 定义一个SoundPool
private SoundPool soundPool;
private HashMap<Integer, Integer> soundMap = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bombBn = findViewById(R.id.bomb);
Button shotBn = findViewById(R.id.shot);
Button arrowBn = findViewById(R.id.arrow);
AudioAttributes attr = new AudioAttributes.Builder().setUsage(
AudioAttributes.USAGE_GAME) // 设置音效使用场景
// 设置音效的类型
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build();
soundPool = new SoundPool.Builder().setAudioAttributes(attr) // 设置音效池的属性
.setMaxStreams(10) // 设置最多可容纳10个音频流
.build(); // ①
// 使用load方法加载指定的音频文件,并返回所加载的音频ID
// 此处使用HashMap来管理这些音频流
soundMap.put(1, soundPool.load(this, R.raw.bomb, 1)); // ②
soundMap.put(2, soundPool.load(this, R.raw.shot, 1));
soundMap.put(3, soundPool.load(this, R.raw.arrow, 1));
// 定义一个按钮的单击监听器
View.OnClickListener listener = source -> {
int sourceId = source.getId();
// 判断哪个按钮被单击
if (sourceId == R.id.bomb) {
soundPool.play(soundMap.get(1), 1f, 1f, 0, 0, 1f); // ③
} else if (sourceId == R.id.shot) {
soundPool.play(soundMap.get(2), 1f, 1f, 0, 0, 1f);
} else if (sourceId == R.id.arrow) {
soundPool.play(soundMap.get(3), 1f, 1f, 0, 0, 1f);
}
};
bombBn.setOnClickListener(listener);
shotBn.setOnClickListener(listener);
arrowBn.setOnClickListener(listener);
}
}
SoundPool
虽然可以一次性加载多个声音,但由于内存限制,因此应该避免使用 SoundPool
来播放歌曲或者做游戏背景音乐,只有那些短促、密集的声音才考虑使用 SoundPool
进行播放。虽然 SoundPool
比 MediaPlayer
的效率好,但也不是绝对不存在延迟问题,尤其在那些性能不太好的手机中,SoundPool
的延迟问题会更严重。
摘抄至《疯狂Android讲义(第4版)》
网友评论