【问题标题】:Kotlin: call a function every secondKotlin:每秒调用一个函数
【发布时间】:2019-08-29 11:00:57
【问题描述】:

我想为我的游戏创建一个简单的倒计时,当游戏开始时我希望每秒调用一次这个函数:

fun minusOneSecond(){
  if secondsLeft > 0{
     secondsLeft -= 1
     seconds_thegame.text = secondsLeft.toString()
  }
}

我试过了:

var secondsLeft = 15

timer.scheduleAtFixedRate(
   object : TimerTask() {

      override fun run() {
         minusOneSecond()
      }

    },0, 1000
)   // 1000 Millisecond  = 1 second

但不幸的是应用停止了,第二次调用 run 函数

我在 3 周前刚开始使用 android 开发和 Kotlin,到目前为止,我对它了解最多。

在 Xcode 中使用 swift 我使用了这一行,我认为 Kotlin 也可以使用类似的东西

setTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(minusOneSecond), userInfo: nil, repeats: true)

【问题讨论】:

  • runOnUiThread 是必要的,因为 UI 只能从 UI 线程进行操作,并且回调发生在临时后台线程中。
  • 使用AsyncTask 怎么样?使用由循环组成的后台任务,等待一秒钟,然后更新进度。还有一个更新 UI 的 onProgressUpdate() 方法。

标签: android kotlin


【解决方案1】:
var isActionAchieved = false
var secondsPassed = 0

fun cDTimer(){
    if (!isActionAchieved && secondsPassed < 10){  // repeat check if Action NOT Achieved for max of 10 seconds 
        Handler(Looper.getMainLooper()).postDelayed({
            repeatThisFunction()
            repeater()
            secondsPassed++
        }, 1000) //one second till next execution
    }
}

fun repeater(){
    cDTimer()
}

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
【解决方案2】:

我的解决方案

viewModelScope.launch(Dispatchers.IO) {
            while(isActive) {
                when(val response = repository.getApi()) {
                    is NetworkState.Success -> {
                        getAllData.postValue(response.data)
                    }
                    is NetworkState.Error -> this@MainViewModel.isActive = false
                }

                delay(API_CALL_DELAY)
            }
        }

【讨论】:

  • 这不是每秒执行一次。它每秒执行一次,加上执行 while 语句中的代码的时间。
【解决方案3】:
val timer = object: CountDownTimer(10000, 1000) {
    override fun onTick(millisUntilFinished: Long) {
        // do something
    }
    override fun onFinish() {
        // do something
    }
}
timer.start()

您也可以为此目的使用 CountDownTimer。因为这需要两个参数(总时间和间隔时间)
此外,它还提供了一个 on finish 方法来在总时间完成时执行任何任务。

【讨论】:

  • 这不好。它限制您必须提前知道最长时间,并在达到该时间时终止。
【解决方案4】:

我在协程中使用递归,非常简单

  private fun loop() {
    CoroutineScope(IO).launch {
        delay(5000)
        CoroutineScope(Main).launch {
            ManagerToWorker()
            loop()
        }
    }
}

【讨论】:

  • 在溢出之前不会增加堆栈吗?
  • @Antzi 会溢出,但堆栈足够大,评论者在延迟 5 秒后看不到它。
【解决方案5】:

我每秒都在调用我的函数

val handler = Handler()
  handler.postDelayed(object : Runnable {
      override fun run() {
            //Call your function here
            handler.postDelayed(this, 1000)//1 sec delay
        }
}, 0)

【讨论】:

  • 为什么要放0?
【解决方案6】:

问题: Timer 类使用带有队列的后台线程来排队并按顺序执行所有任务。从您的代码中,因为您更新了 UI(更改 minusOneSecond 函数中的 TextView 内容)。这就是为什么应用会抛出以下异常并导致应用崩溃的原因。

android.view.ViewRootImpl$CalledFromWrongThreadException: 只有 创建视图层次结构的原始线程可以触及其视图。

解决方案:有很多方法可以完成你的任务,但我更喜欢使用@987654322 中的 post()postDelayed() 方法@ 班级。因为它简单易懂。

val mainHandler = Handler(Looper.getMainLooper())

mainHandler.post(object : Runnable {
    override fun run() {
        minusOneSecond()
        mainHandler.postDelayed(this, 1000)
    }
})

更新:来自作者关于如何从 Handler 暂停/恢复任务的评论。这是一个例子。

class MainActivityKt : AppCompatActivity() {

    lateinit var mainHandler: Handler

    private val updateTextTask = object : Runnable {
        override fun run() {
            minusOneSecond()
            mainHandler.postDelayed(this, 1000)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Your logic code
        ...
        mainHandler = Handler(Looper.getMainLooper())
    }

    override fun onPause() {
        super.onPause()
        mainHandler.removeCallbacks(updateTextTask)
    }

    override fun onResume() {
        super.onResume()
        mainHandler.post(updateTextTask)
    }

    fun minusOneSecond() {
        if secondsLeft > 0 {
            secondsLeft -= 1
            seconds_thegame.text = secondsLeft.toString()
        }
    }
}

【讨论】:

    【解决方案7】:

    请使用

    inline fun Timer.schedule(
        time: Date, 
        period: Long, 
        crossinline action: TimerTask.() -> Unit
    ): TimerTask
    

    参考:https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/java.util.-timer/schedule.html

    【讨论】:

      【解决方案8】:

      我正在使用此代码每分钟更新一次时钟

       fixedRateTimer("timer", false, 0L, 60 * 1000) {
           this@FullscreenActivity.runOnUiThread {
               tvTime.text = SimpleDateFormat("dd MMM - HH:mm", Locale.US).format(Date())
           }
       }
      

      所以你必须使用参数 1000 而不是 60*1000 运行它

      【讨论】:

        猜你喜欢
        • 2020-06-25
        • 2014-12-24
        • 1970-01-01
        • 2011-11-06
        • 2011-03-09
        • 1970-01-01
        • 1970-01-01
        • 2015-06-26
        • 2018-05-17
        相关资源
        最近更新 更多