android - 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)
}
解决方案
是的,假设您加载了足够多的独特声音,您最终会耗尽内存。SoundPool
正在以未压缩的形式存储它加载的声音。而且,它必须使它们保持这种状态,因为play()
请求可能随时进入(据它所知)。 SoundPool
不会在没有明确调用release()
or的情况下释放声音unload()
。
再说一次,如果你没有很多声音,那么你可能不会耗尽内存,因为短的声音片段相对较小。
另请注意,如果您将相同的资源 ID 传递给SoundLoadManager.load()
两次,您的代码将覆盖现有的声音 ID ( soundPoolMap
),这意味着您永远无法调用unload()
该特定声音。MaybeSoundPool
足够聪明,不会两次加载声音,它只是返回原始声音 ID;我不知道。我得测试一下。
因此,即使您只有少量声音文件,如果重复播放它们,您最终也可能会耗尽内存(因为您从不调用unload()
)。
或者因为它在函数内部创建声音我不需要担心它?
不正确!这些不是局部变量。借助SoundPool
对象,您的声音范围比这更大;该功能与它无关。
推荐阅读
- terraform - terraform 和 list,split 返回未知索引的最后一个元素
- visual-studio-code - 按文件名模式的 VSCode 语言扩展
- google-apps-script - 从 Docs Apps 脚本中的 PositionedImage 获取 Alt 信息
- html - font-size 62.5% 有什么作用?
- python - “列表索引必须是整数或切片,而不是字符串”,但它们实际上是整数
- ruby-on-rails - 如何将我的 Rails 应用程序发布到 github 页面
- java - 如何在没有中断的情况下停止while循环
- java - 当整个代码基于 gradle 时,如何执行子任务代码
- matlab - 以编程方式取消 ROI 选择
- python - 如何从 kismet 读取 pcapng 文件流