SoundPool

一、SoundPool 特点

  • 适合场景:短音效(<5秒)、游戏音效、频繁播放的音效

  • 优势:低延迟、可同时播放多个音效、音量独立控制

  • 限制:不适合长音频(内存占用大)

二、完整实现步骤

步骤1:添加音频文件到项目

  1. res 目录下创建 raw 文件夹:

    复制代码
    app/src/main/res/raw/
  2. .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
复制代码
相关推荐
鹏多多1 小时前
flutter-使用url_launcher打开链接/应用/短信/邮件和评分跳转等
android·前端·flutter
2501_915921431 小时前
iOS 性能分析工具全景解析,构建从底层诊断到真机监控的多层级性能分析体系
android·ios·小程序·https·uni-app·iphone·webview
zhixingheyi_tian1 小时前
TestDFSIO 之 热点分析
android·java·javascript
2501_915909061 小时前
如何防止 IPA 被反编译,从攻防视角构建一套真正有效的 iOS 成品保护体系
android·macos·ios·小程序·uni-app·cocoa·iphone
触想工业平板电脑一体机1 小时前
【触想智能】工业触控一体机在工业应用中扮演的角色以及其应用场景分析
android·大数据·运维·电脑·智能电视
克喵的水银蛇1 小时前
Flutter 入门实战:从零搭建跨平台 HelloWorld 应用(适配鸿蒙 / 安卓 /iOS)
android·flutter·harmonyos
三七吃山漆1 小时前
攻防世界——wzsc_文件上传
android·网络安全·web·ctf
python百炼成钢1 小时前
49.Linux音频驱动
android·linux·音视频