首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我可以使用kotlin-coroutines定期运行任务吗?

我可以使用kotlin-coroutines定期运行任务吗?
EN

Stack Overflow用户
提问于 2022-01-27 08:53:15
回答 1查看 1K关注 0票数 1

似乎我可以使用代码A定期运行任务。

你知道,soundDb()可以每100毫秒启动一次,就像定期运行一样。

使用kotlin-coroutines定期运行任务是一种好方法吗?

码A

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
 viewModelScope.launch {
      soundDbFlow().runningAverage().collect {
        println("Average = $it") // do something with it
      }
   }

他说:你知道我可以用Timer().scheduleAtFixedRate定期在后台线程中获取信息,就像代码D一样,它是Timer().scheduleAtFixedRateFlow之间的吗?

码D

代码语言:javascript
复制
   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")
    }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-01-27 09:23:38

重复任务的方法(launch +循环)本身并不坏,但问题在于您希望这个协同机制如何影响应用程序的其余部分。

为了这个问题,很难说这是一个示例,还是您的实际代码。如果是实际代码,则用例不是典型的“定期任务运行”:

  • 它有固定的迭代次数
  • 它只在执行

的末尾有副作用

这表明将此代码编写为挂起函数可能更有意义:

代码语言:javascript
复制
suspend fun computeCurrent(): Double {
    var b = 0.0
    repeat(5) {
        b += soundDb()
        delay(100)
    }
    return b / 5.0
}

然后像这样使用它,以便更清楚地说明使用结果的地方:

代码语言:javascript
复制
viewModelScope.launch {
    val b = computeCurrent()
    myInfo.value = "$b OK Asyn  ${a++}"
}

也许您实际上不需要以异步的方式启动协同器(这可能取决于您如何进行其他类似的调用)。

如果您需要定期(而不仅仅是在最后)从一个具有定期任务的长期运行的协同机制中获取信息,您可能需要考虑构建一个Flow,并收集它来应用这些副作用:

代码语言:javascript
复制
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)
    }
}

然后它的用法可能是:

代码语言:javascript
复制
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不会与结构化并发集成,需要手动管理取消。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70875683

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档