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
复制代码
相关推荐
、BeYourself11 分钟前
GridLayoutManager 网格布局与 RecyclerView 拖拽侧滑实战
android·android-studio
Kapaseker13 分钟前
如何写出高性能的Java Stream
android·java
tangweiguo0305198719 分钟前
Android 插件化开发完全指南(Kotlin DSL/Gradle KTS 配置)
android·kotlin
八眼鱼20 分钟前
uniappx 安卓拍照,添加水印后比例正常
android
野生风长22 分钟前
从零开始的C语言:文件操作与数据管理(下)(fseek,ftell,rewind,文件的编译和链接)
android·java·c语言·开发语言·visual studio
2501_9160074724 分钟前
Xcode 在 iOS 上架中的定位,多工具组合
android·macos·ios·小程序·uni-app·iphone·xcode
游戏开发爱好者826 分钟前
uni-app 项目在 iOS 上架过程中常见的问题与应对方式
android·ios·小程序·https·uni-app·iphone·webview
2501_915106321 小时前
iOS 抓包工具在不同场景的实际作用
android·macos·ios·小程序·uni-app·cocoa·iphone
草莓熊Lotso1 小时前
C++ 异常完全指南:从语法到实战,优雅处理程序错误
android·java·开发语言·c++·人工智能·经验分享·后端
モンキー・D・小菜鸡儿1 小时前
Android BottomSheetBehavior 使用详解
android·kotlin