Quiero crear una cuenta regresiva simple para mi juego, cuando el juego comience, quiero que se llame a esta función cada segundo:
fun minusOneSecond(){
if secondsLeft > 0{
secondsLeft -= 1
seconds_thegame.text = secondsLeft.toString()
}
}
Intenté esto:
var secondsLeft = 15
timer.scheduleAtFixedRate(
object : TimerTask() {
override fun run() {
minusOneSecond()
}
},0, 1000
) // 1000 Millisecond = 1 second
Pero, lamentablemente, la aplicación se detiene, la segunda vez que se llama a la función de ejecución
Empecé con el desarrollo de Android y Kotlin hace 3 semanas y hasta ahora lo entiendo al máximo.
Con swift en Xcode uso esta línea y pensé que algo similar funcionaría con Kotlin
setTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(minusOneSecond), userInfo: nil, repeats: true)
hijo truong
Problema: Temporizador class usa un subproceso de fondo con una cola para poner en cola y ejecutar todas las tareas secuencialmente. Desde su código, porque actualiza la interfaz de usuario (cambiando el contenido de TextView en minusOneSecond
función). Es por eso que la aplicación lanza la siguiente excepción y hace que su aplicación se bloquee.
android.view.ViewRootImpl$CalledFromWrongThreadException: solo el subproceso original que creó una jerarquía de vistas puede tocar sus vistas.
Solución: Hay muchas maneras de lograr su tarea, pero prefiero usar correo() y postretrasado() método de Manipulador clase. Porque es simple y fácil de entender.
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post(object : Runnable {
override fun run() {
minusOneSecond()
mainHandler.postDelayed(this, 1000)
}
})
Actualizar: Del comentario del autor sobre cómo pausar/reanudar la tarea de Handler. Aquí hay un ejemplo.
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()
}
}
}
Estoy usando este código para actualizar un reloj cada minuto.
fixedRateTimer("timer", false, 0L, 60 * 1000) {
this@FullscreenActivity.runOnUiThread {
tvTime.text = SimpleDateFormat("dd MMM - HH:mm", Locale.US).format(Date())
}
}
así que tienes que ejecutarlo con paratemer 1000
en vez de 60*1000
val timer = object: CountDownTimer(10000, 1000) {
override fun onTick(millisUntilFinished: Long) {
// do something
}
override fun onFinish() {
// do something
}
}
timer.start()
También puede usar CountDownTimer para este propósito. Como esto toma dos parámetros (el tiempo total y el intervalo de tiempo)
Además, también proporciona un método de finalización para realizar cualquier tarea cuando finaliza el tiempo total.
-
Esto no está bien. Te limita a tener que saber la cantidad máxima de tiempo por adelantado y terminará cuando se alcance ese tiempo.
– Juan
29 de abril de 2021 a las 16:08
sandy genedy
por favor use
inline fun Timer.schedule(
time: Date,
period: Long,
crossinline action: TimerTask.() -> Unit
): TimerTask
referencia: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/java.util.-timer/schedule.html
Estoy llamando a mi función cada segundo así
val handler = Handler()
handler.postDelayed(object : Runnable {
override fun run() {
//Call your function here
handler.postDelayed(this, 1000)//1 sec delay
}
}, 0)
-
¿Por qué poner 0 allí?
– Arbaz.in
7 de octubre de 2021 a las 4:40
Thiago
Mi solución
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)
}
}
-
¿Por qué poner 0 allí?
– Arbaz.in
7 de octubre de 2021 a las 4:40
hussain tarek
Estoy usando recursividad con Coroutine es muy simple
private fun loop() {
CoroutineScope(IO).launch {
delay(5000)
CoroutineScope(Main).launch {
ManagerToWorker()
loop()
}
}
}
-
¿Eso no haría crecer la pila hasta el desbordamiento?
– Antzi
6 de agosto de 2020 a las 7:57
-
@Antzi se desbordará, pero la pila es lo suficientemente grande como para que el comentarista no pueda verla con 5 segundos de retraso.
–Dominik Murzynowski
7 de julio de 2021 a las 7:32
-
La recursividad a menudo conduce a desbordamientos de pila inesperados. Si bien esta solución puede “funcionar” en situaciones limitadas, eventualmente fallará (quizás después de muchas horas o incluso días dado el retraso de 5000 ms).
– Mate
28 de marzo a las 15:45
-
Mientras pienso más en esto, es posible que técnicamente no cause un desbordamiento de la pila, ya que cada llamada a CoroutineScope(Main).launch crearía un ejecutable separado. Sin embargo, aún conduciría a una pérdida de memoria en la mayoría de los casos, ya que Stacktrace Recovery recordaría cada llamada. (Si ManagerToWorker() lanzó una excepción, ¡el seguimiento de la pila podría ser enorme!) github.com/Kotlin/kotlinx.coroutines/blob/master/docs/topics/…
– Mate
15 abr a las 16:21
Posible duplicado de ¿Cómo llamar a una función después de un retraso en Kotlin?
– no iniciar sesión
8 de abril de 2019 a las 10:14
RunOnUiThread es necesario porque la interfaz de usuario solo se puede manipular desde un subproceso de interfaz de usuario y la devolución de llamada ocurre en un subproceso de fondo temporal.
– Eugenio Petrenko
08/04/2019 a las 10:15
¿Qué hay de usar un AsyncTask ? Con una tarea en segundo plano compuesta por un bucle, que espera un segundo y luego actualiza el progreso. Y un método onProgressUpdate() que actualiza su interfaz de usuario.
– vincrichaud
8 de abril de 2019 a las 10:17