首页 > 解决方案 > SoundPool 内存不足?

问题描述

我有以下课程。

class SoundLoadManager(val ctx:Context) {
    var scheduled = 0
    var loaded = 0
    val sndPool: SoundPool
    val soundPoolMap = mutableMapOf<Int,Int>()
    init {
        sndPool =
                if (Build.VERSION.SDK_INT >=
                        Build.VERSION_CODES.LOLLIPOP) {
                    SoundPool.Builder()
                            .setMaxStreams(10)
                            .setAudioAttributes(
                                    AudioAttributes.Builder()
                                            .setUsage(
                                                    AudioAttributes.USAGE_MEDIA)
                                            .setContentType(
                                                    AudioAttributes.CONTENT_TYPE_MUSIC)
                                            .build()
                            ).build()
                } else {
                    SoundPool(10,
                            AudioManager.STREAM_MUSIC,
                            100)
                }
        sndPool.setOnLoadCompleteListener({
            sndPool, sampleId, status ->
            if(status != 0) {
                Log.e("LOG",
                        "Sound could not be loaded")
            } else {
                Log.i("LOG", "Loaded sample " +
                        sampleId + ", status = " +
                        status)
            }
            loaded++
        })
    }
    fun load(resourceId:Int) {
        scheduled++
        soundPoolMap[resourceId] =
                sndPool.load(ctx, resourceId, 1)
    }
    fun allLoaded() = scheduled == loaded
    fun play(rsrcId: Int, loop: Boolean):Int {
        return soundPoolMap[rsrcId]?.run {
            val audioManager = ctx.getSystemService(
                    Context.AUDIO_SERVICE) as AudioManager
            val curVolume = audioManager.
                    getStreamVolume(
                            AudioManager.STREAM_MUSIC)
            val maxVolume = audioManager.
                    getStreamMaxVolume(
                            AudioManager.STREAM_MUSIC)
            val leftVolume = 1f * curVolume / maxVolume
            val rightVolume = 1f * curVolume / maxVolume
            val priority = 1
            val noLoop = if(loop) -1 else 0
            val normalPlaybackRate = 1f
            sndPool.play(this, leftVolume, rightVolume,
                    priority, noLoop, normalPlaybackRate)
        } ?: -1
    }
}

并按如下方式使用它。

fun playHarmonicInterval(baseInterval: String, scaleDegree: String? ){



           soundLoadManager.load(getResources().getIdentifier(baseInterval, "raw", getPackageName()));
           soundLoadManager.load(getResources().getIdentifier(scaleDegree, "raw", getPackageName()))
            // more ...

        timer = Timer()
        timer.schedule(object : TimerTask() {
            override fun run() {
                if(soundLoadManager.allLoaded()){
                    soundLoadManager.play(getResources().getIdentifier(baseInterval, "raw", getPackageName()), false)
                    soundLoadManager.play(getResources().getIdentifier(scaleDegree, "raw", getPackageName()), false)
                    timer.cancel();

                }
            }
        }, 0, 100)


    }

我的问题是我是否需要担心我的 SoundLoadManager 这样做会占用大量内存并导致应用程序崩溃。或者因为它在函数内部创建声音我不需要担心它?

在下面的回复之后,我已将我的代码更新为以下内容。它似乎仍然可以正常工作。

fun playHarmonicInterval(baseInterval: String, scaleDegree: String? ){

        soundLoadManager = SoundLoadManager(this)


         var  value1 = soundLoadManager.load(getResources().getIdentifier(baseInterval, "raw", getPackageName()));
         var value2 =  soundLoadManager.load(getResources().getIdentifier(scaleDegree, "raw", getPackageName()))


        timer = Timer()
        timer.schedule(object : TimerTask() {
            override fun run() {
                if(soundLoadManager.allLoaded()){
                    soundLoadManager.play(getResources().getIdentifier(baseInterval, "raw", getPackageName()), false)
                    soundLoadManager.play(getResources().getIdentifier(scaleDegree, "raw", getPackageName()), false)
                    timer.cancel();
                    soundLoadManager.sndPool?.release()
                    soundLoadManager.sndPool = null;

                }
            }
        }, 0, 100)


    }

标签: android

解决方案


是的,假设您加载了足够多的独特声音,您最终会耗尽内存。SoundPool正在以未压缩的形式存储它加载的声音。而且,它必须使它们保持这种状态,因为play()请求可能随时进入(据它所知)。 SoundPool不会在没有明确调用release()or的情况下释放声音unload()

再说一次,如果你没有很多声音,那么你可能不会耗尽内存,因为短的声音片段相对较小。

另请注意,如果您将相同的资源 ID 传递给SoundLoadManager.load()两次,您的代码将覆盖现有的声音 ID ( soundPoolMap),这意味着您永远无法调用unload()该特定声音。MaybeSoundPool足够聪明,不会两次加载声音,它只是返回原始声音 ID;我不知道。我得测试一下。

因此,即使您只有少量声音文件,如果重复播放它们,您最终也可能会耗尽内存(因为您从不调用unload())。

或者因为它在函数内部创建声音我不需要担心它?

不正确!这些不是局部变量。借助SoundPool对象,您的声音范围比这更大;该功能与它无关。


推荐阅读