似乎我可以使用代码A定期运行任务。
你知道,soundDb()可以每100毫秒启动一次,就像定期运行一样。
使用kotlin-coroutines定期运行任务是一种好方法吗?
码A
fun calCurrentAsyn() {
viewModelScope.launch {
var b = 0.0
for (i in 1..5) {
b = b + soundDb()
delay(100)
}
b = b / 5.0
myInfo.value = b.toString() + " OK Asyn " + a++.toString()
}
}
suspend fun soundDb(): Double {
var k=0.0
for (i in 1..500000000){
k=k+i
}
return k
}添加的内容:
致乔佛里:谢谢!
他:我知道B码是比较好的风格,A码和B码的执行效果会一样吗?
码B
viewModelScope.launch {
val b = computeCurrent()
myInfo.value = "$b OK Asyn ${a++}"
}
suspend fun computeCurrent(): Double {
var b = 0.0
repeat(5) {
b += soundDb()
delay(100)
}
return b / 5.0
}
suspend fun soundDb(): Double {
var k=0.0
for (i in 1..500000000){
k=k+i
}
return k
}他说:我希望定期从一个有周期性任务的长期运行的协同中获得信息,我如何取消流soundDbFlow().runningAverage()?
代码C
viewModelScope.launch {
soundDbFlow().runningAverage().collect {
println("Average = $it") // do something with it
}
}他说:你知道我可以用Timer().scheduleAtFixedRate定期在后台线程中获取信息,就像代码D一样,它是Timer().scheduleAtFixedRate和Flow之间的吗?
码D
private fun startTimer() {
timer = Timer()
timer.scheduleAtFixedRate(timerTask {
recordingTime = recordingTime + 1
val temp = fromCountToTimeByInterval(recordingTime, 10)
_timeElapse.postValue(temp)
}, 0, 10)
}
private fun stopTimer() {
timer.cancel()
recordingTime = 0
_timeElapse.postValue("00:00.00")
}发布于 2022-01-27 09:23:38
重复任务的方法(launch +循环)本身并不坏,但问题在于您希望这个协同机制如何影响应用程序的其余部分。
为了这个问题,很难说这是一个示例,还是您的实际代码。如果是实际代码,则用例不是典型的“定期任务运行”:
的末尾有副作用
这表明将此代码编写为挂起函数可能更有意义:
suspend fun computeCurrent(): Double {
var b = 0.0
repeat(5) {
b += soundDb()
delay(100)
}
return b / 5.0
}然后像这样使用它,以便更清楚地说明使用结果的地方:
viewModelScope.launch {
val b = computeCurrent()
myInfo.value = "$b OK Asyn ${a++}"
}也许您实际上不需要以异步的方式启动协同器(这可能取决于您如何进行其他类似的调用)。
如果您需要定期(而不仅仅是在最后)从一个具有定期任务的长期运行的协同机制中获取信息,您可能需要考虑构建一个Flow,并收集它来应用这些副作用:
import kotlinx.coroutines.flow.*
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
fun soundDbFlow(period: Duration = 100.milliseconds) = flow {
while (true) {
emit(soundDb())
delay(period)
}
}
fun Flow<Double>.runningAverage(): Flow<Double> = flow {
var valuesCount = 0
var sum = 0.0
collect { value ->
sum += value
valuesCount++
emit(sum / valuesCount)
}
}然后它的用法可能是:
viewModelScope.launch {
soundDbFlow().take(5).runningAverage().collect {
println("Average = $it") // do something with it
}
}关于经修订的问题:
我知道代码B是更好的风格,代码A和代码B之间的执行效果会一样吗?
代码A和代码B的行为相同。我的观点实际上是关于样式的一半,因为使它成为一个简单的挂起函数可以清楚地(对您和读者),它只返回一个值。这似乎是一个错误,您在新添加的soundDb()函数中也犯了错误,我不确定循环不是流,并且您只从这些函数返回一个值(没有多次更新)。
我的另一半观点是,由于它只是您更新的一个值,所以它甚至不需要在一个长期运行的协同机制中运行。您可以在需要时将其与其他挂起代码进行集成。
如何取消soundDbFlow().runningAverage()流?
如果取消了收集协同线(通过您launch编辑的作业或取消整个viewModelScope --当不需要组件时自动发生),则流会自动取消。如果您使用早期结束集合的终端操作符(如first()、takeWhile()、take(n).collect { .. }等),则流也会被取消。
您知道我可以使用Timer().scheduleAtFixedRate在后台线程中定期获取信息,就像代码D一样,这是Timer().scheduleAtFixedRate和Flow之间的关系吗?
这取决于你的诚实。如果你已经在使用协同线了,我个人会倾向于流的方法。scheduleAtFixedRate不会与结构化并发集成,需要手动管理取消。
https://stackoverflow.com/questions/70875683
复制相似问题