一、SoundPool 特点
-
适合场景:短音效(<5秒)、游戏音效、频繁播放的音效
-
优势:低延迟、可同时播放多个音效、音量独立控制
-
限制:不适合长音频(内存占用大)
二、完整实现步骤
步骤1:添加音频文件到项目
-
在
res目录下创建raw文件夹:app/src/main/res/raw/ -
将
.ogg文件放入(如:explosion.ogg,click.ogg)
步骤2:初始化 SoundPool(现代方式 - API 21+)
Kotlin
class SoundActivity : AppCompatActivity() {
// 声明变量
private lateinit var soundPool: SoundPool
private val soundMap = HashMap<String, Int>() // 存储音效ID
private var loaded = false // 加载状态标志
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sound)
initializeSoundPool()
loadSounds()
}
private fun initializeSoundPool() {
// 配置音频属性
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME) // 用途:游戏
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) // 内容类型:音效
.build()
// 创建 SoundPool
soundPool = SoundPool.Builder()
.setMaxStreams(10) // 最大同时播放流数
.setAudioAttributes(audioAttributes) // 音频属性
.build()
// 设置加载完成监听器
soundPool.setOnLoadCompleteListener { _, sampleId, status ->
if (status == 0) {
loaded = true
Log.d("SoundPool", "音效加载完成,ID: $sampleId")
}
}
}
}
步骤3:加载多个音效
Kotlin
private fun loadSounds() {
// 加载音效文件(参数:上下文, 资源ID, 优先级)
soundMap["explosion"] = soundPool.load(this, R.raw.explosion, 1)
soundMap["click"] = soundPool.load(this, R.raw.click, 1)
soundMap["coin"] = soundPool.load(this, R.raw.coin_collect, 1)
soundMap["jump"] = soundPool.load(this, R.raw.jump, 1)
}
步骤4:播放音效的完整方法
Kotlin
/**
* 播放音效
* @param soundName 音效名称(在soundMap中的key)
* @param loop 循环次数:0=不循环,-1=无限循环
* @param rate 播放速率:1.0=正常,0.5=半速,2.0=倍速
*/
fun playSound(soundName: String, loop: Int = 0, rate: Float = 1.0f) {
if (!loaded) {
Log.w("SoundPool", "音效尚未加载完成")
return
}
val soundId = soundMap[soundName]
if (soundId == null) {
Log.e("SoundPool", "未找到音效: $soundName")
return
}
// 播放音效
val streamId = soundPool.play(
soundId, // 音效ID
1.0f, // 左声道音量(0.0-1.0)
1.0f, // 右声道音量(0.0-1.0)
1, // 优先级(0=最低)
loop, // 循环次数
rate // 播放速率
)
if (streamId == 0) {
Log.e("SoundPool", "播放失败")
}
}
// 使用示例
playSound("click") // 播放点击音效
playSound("explosion") // 播放爆炸音效
playSound("coin", loop = 0, rate = 1.2f) // 加速播放
步骤5:音效控制方法
Kotlin
/**
* 暂停指定音效
*/
fun pauseSound(streamId: Int) {
soundPool.pause(streamId)
}
/**
* 恢复播放指定音效
*/
fun resumeSound(streamId: Int) {
soundPool.resume(streamId)
}
/**
* 停止指定音效
*/
fun stopSound(streamId: Int) {
soundPool.stop(streamId)
}
/**
* 设置音量
* @param streamId 流ID(play()方法的返回值)
* @param leftVolume 左声道音量 0.0-1.0
* @param rightVolume 右声道音量 0.0-1.0
*/
fun setVolume(streamId: Int, leftVolume: Float, rightVolume: Float) {
soundPool.setVolume(streamId, leftVolume, rightVolume)
}
/**
* 设置播放速率(0.5-2.0)
*/
fun setRate(streamId: Int, rate: Float) {
soundPool.setRate(streamId, rate)
}
步骤6:资源管理
Kotlin
class SoundActivity : AppCompatActivity() {
// ... 其他代码 ...
/**
* 手动卸载音效
*/
fun unloadSound(soundName: String) {
val soundId = soundMap[soundName]
if (soundId != null) {
soundPool.unload(soundId)
soundMap.remove(soundName)
}
}
/**
* 卸载所有音效
*/
fun unloadAllSounds() {
soundMap.values.forEach { soundId ->
soundPool.unload(soundId)
}
soundMap.clear()
}
override fun onPause() {
super.onPause()
// 暂停所有音效
soundPool.autoPause()
}
override fun onResume() {
super.onResume()
// 恢复音效
soundPool.autoResume()
}
override fun onDestroy() {
// 释放所有资源
unloadAllSounds()
soundPool.release()
super.onDestroy()
}
}
三、SoundPool 操作的耗时性分析
1. 耗时操作(应该异步)
Kotlin
// ❌ 这些操作会阻塞主线程,应该异步执行
// 加载音频文件(最耗时)
soundPool.load(context, R.raw.sound, 1) // 可能耗时 50-500ms
// 释放资源
soundPool.release() // 可能耗时 10-100ms
// 初始化 SoundPool(较新版本)
SoundPool.Builder().build() // 可能耗时 10-50ms
2. 非耗时操作(可以在主线程)
Kotlin
// ✅ 这些操作很快,可以在主线程执行
// 播放音效
soundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f) // < 1ms
// 控制播放
soundPool.pause(streamId) // < 1ms
soundPool.resume(streamId) // < 1ms
soundPool.stop(streamId) // < 1ms
// 设置参数
soundPool.setVolume(streamId, 0.5f, 0.5f) // < 1ms
soundPool.setRate(streamId, 1.5f) // < 1ms