[英]Kotlin: call a function every second
我想為我的游戲創建一個簡單的倒計時,當游戲開始時,我希望每秒調用一次 function:
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
但不幸的是,該應用程序停止了,第二次運行 function 被調用
3 周前,我剛開始使用 android 開發和 Kotlin,到目前為止,我了解最多。
在 Xcode 中使用 swift 我使用這條線,我認為類似的東西適用於 Kotlin
setTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(minusOneSecond), userInfo: nil, repeats: true)
問題: Timer類使用帶有隊列的后台線程來排隊並按順序執行所有任務。 從您的代碼中,因為您更新了 UI(在minusOneSecond
函數中更改 TextView 內容)。 這就是應用程序拋出以下異常並使您的應用程序崩潰的原因。
android.view.ViewRootImpl$CalledFromWrongThreadException:只有創建視圖層次結構的原始線程才能觸摸其視圖。
解決方案:有很多方法可以實現您的任務,但我更喜歡使用Handler類中的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()
}
}
}
我正在使用此代碼每分鍾更新一個時鍾
fixedRateTimer("timer", false, 0L, 60 * 1000) {
this@FullscreenActivity.runOnUiThread {
tvTime.text = SimpleDateFormat("dd MMM - HH:mm", Locale.US).format(Date())
}
}
所以你必須用參數1000
而不是60*1000
來運行它
val timer = object: CountDownTimer(10000, 1000) {
override fun onTick(millisUntilFinished: Long) {
// do something
}
override fun onFinish() {
// do something
}
}
timer.start()
您也可以為此目的使用 CountDownTimer。 因為這需要兩個參數(總時間和間隔時間)
此外,它還提供了一個 on finish 方法來在總時間完成后執行任何任務。
請用
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
我每秒都在像這樣調用我的函數
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
//Call your function here
handler.postDelayed(this, 1000)//1 sec delay
}
}, 0)
我在協程中使用遞歸非常簡單
private fun loop() {
CoroutineScope(IO).launch {
delay(5000)
CoroutineScope(Main).launch {
ManagerToWorker()
loop()
}
}
}
我的解決方案
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)
}
}
var isActionAcheived = false
var secondsPassed = 0
fun cDTimer(){
if (!isActionAcheived && secondsPassed < 10){ // repeat check if Action NOT Aceived for max of 10 seconds
Handler(Looper.getMainLooper()).postDelayed({
repeatThisFunction()
repeater()
secondsPassed++
}, 1000) //one second till next execution
}
}
fun repeater(){
cDTimer()
}
如果您使用任何后台任務或后台服務,請嘗試此代碼
val timer = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate({
Log.d("RUNNING ","Thread")
},0,10,TimeUnit.SECONDS)
如果您使用 UI 其他人,例如更新 UI 布局,請嘗試此代碼
val timer = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate({
Log.d("RUNNING ","BACKGROUN Thread")
runOnUiThread {
Log.d("RUNNING ","Update UI Thread")
btnUpdate.setText(System.currentTimeMillis().toString())
}
},0,1,TimeUnit.SECONDS)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.