提问人:Sebastian Lore 提问时间:8/27/2022 更新时间:9/1/2022 访问量:246
是否需要使用原子或其他同步
Is there a need to use atomics or other synchronization
问:
我是 Compose 和 Android 开发的新手。在使用 Compose 构建玩具项目时,我遇到了一个问题。我需要从资产中加载 ~100 个短声音才能通过 . 我想异步加载(否则为什么我们需要回调)。SoundPool
SoundPool.load()
viewmodel 中的代码:
private val TAG = javaClass.simpleName
private val soundPool: SoundPool
private val soundsInProgress = AtomicInteger(0)
var sounds: List<Sound> by mutableStateOf(listOf())
private set
init {
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build()
soundPool = SoundPool.Builder()
.setMaxStreams(3)
.setAudioAttributes(audioAttributes)
.build()
loadSoundsFromAssets()
}
private fun loadSoundsFromAssets() {
val assetManager = getApplication<MyApp>().applicationContext.assets
val sounds = assetManager.list("sounds/")!!.map(this::parseSound)
soundsInProgress.set(sounds.size)
soundPool.setOnLoadCompleteListener { _, id, status ->
if (status == 0) {
val left = soundsInProgress.decrementAndGet()
Log.i(TAG, "Loaded 1 sound, $left in progress")
if (left == 0) {
sounds = sounds
}
} else {
Log.e(TAG, "Could not load sound $id, status is $status")
}
}
sounds.forEach {
val soundId = soundPool.load(assetManager.openFd("sounds/" + it.fileName), 0)
it.soundId = soundId
}
}
我在这里的目标是跟踪声音预加载的进度。
问题是:在这里使用原子是矫枉过正,还是没有原子是安全的?
另外,我如何观察 ?MutableState 不会按预期工作。soundsInProgress
答:
这取决于此方法是阻塞调用还是非阻塞调用。soundPool.load()
val soundId = soundPool.load(assetManager.openFd("sounds/" + it.fileName), 0)
如果这是一个阻塞调用,那么你不需要原子整数,否则你需要原子整数。
我查找了SoundPool的源代码,似乎它有一个本机(C / C++)实现,可能是一个阻塞调用。所以你可能不需要原子整数。- 请务必先验证这一点。
您可以通过在 setOnLoadCompleteListener 中打印声音文件名来验证这一点,并检查它是否按照从 assetmanager 加载时在列表中的顺序打印。sounds
SoundPool.load()
确实异步加载,即使此处未记录。我根据经验对其进行了测试(在触发回调之前调用返回),并且文档肯定会提到需要从主线程中调用(另外,正如您提到的,如果它是同步调用,回调就没有意义)。load
load
可以假设回调一个接一个地发生,因为它们发生在主线程上(同样没有记录!),所以理论上你不需要一个,但一旦我解释了我对 ui 的建议,我就会回到那个语句。AtomicInteger
对于撰写 UI,我将使用 一个将回调的进度传达给 UI。mutableStateOf
像这样的东西:
private data class SoundProgress(val loaded: Int, val total: Int)
private var progress by mutableStateOf(SoundProgress(0, 0))
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text(text = "${progress.loaded}/${progress.total}")
}
在回调中,你有:
val count = AtomicInteger()
soundPool.setOnLoadCompleteListener { _, id, status ->
if (status == 0) {
progress = SoundProgress(count.incrementAndGet(), sounds.size)
} else {
Log.e(TAG, "Could not load sound $id, status is $status")
}
}
请注意,我使用了 for the count 变量。由于所有这些都在主线程上运行(实际的加载操作除外),因此简单的操作很可能会这样做。在我编写异步代码的漫长岁月中,我经常遇到意想不到的行为,所以虽然我会说 inc 操作总是以原子方式发生,但为了安全起见,我使用 regardless 只是为了安全起见。由于使用它不会带来(性能/内存)损失,并且由于无法消除 SoundPool 在后台使用线程池的可能性,因此我会采用保守的方法。AtomicInteger
int
AtomicInteger
int
顺便说一句,尽量省略!运营商。虽然它们很方便,但它也是一种反模式。
而不是:
val sounds = assetManager.list("sounds/")!!.map(this::parseSound)
使用这个:
val sounds = assetManager.list("sounds")?.map(this::parseSound) ?: listOf()
有点啰嗦,但你永远不会得到 NPE。
你不需要这里。无论如何,变量都会在负载时递增,并且由于执行的唯一操作是递增,因此整个点都着火了,不是吗?当你需要观察一个特定的变量,以精确测量它的增量-递减时,你就会使用这些东西,而主流过程本质上取决于它的值。AtomicInteger
AtomicInteger
在您的情况下,您所要做的就是记录/显示已成功加载的文件数量。它不会对任何事情产生任何影响。无论如何,我看不出任何理由标准在某种情况下无法正确更新,但同样,不会花费你任何费用,所以随心所欲地拥抱你的偏执狂。int
AtomicInteger
就观察部分而言,其中一个答案鼓励您使用 .我不鼓励这样做,因为最简单的方法几乎总是最好的方法。MutableStateFlow
我假设,从调用 ,您提供的有关加载资产部分的逻辑可以方便地放置在您的 viewModel 中。现在,只需在模型中创建一个对象。然后,像这样更新它init
Mutablestate<T>
var progress by mutableStateOf(0f)
private fun loadSoundsFromAssets() {
val assetManager = getApplication<MyApp>().applicationContext.assets
val sounds = assetManager.list("sounds/")!!.map(this::parseSound)
soundsInProgress.set(sounds.size)
soundPool.setOnLoadCompleteListener { _, id, status ->
if (status == 0) {
val left = soundsInProgress--.toFloat()
progress = left / sounds.size
if (left == 0) {
sounds = sounds // WHAT DOES THIS EVEN MEAN?
}
} else {
Log.e(TAG, "Could not load sound $id, status is $status")
}
}
sounds.forEach {
val soundId = soundPool.load(assetManager.openFd("sounds/" + it.fileName), 0)
it.soundId = soundId
}
}
然后,在可组合作用域内观察这一点。我不明白为什么这行不通。
{
Text("${viewModel.progress}")
}
评论
val left = soundsInProgress--.toFloat()
评论