Kotlin Android倒计时视图开发:解决代码崩溃问题并实现可动态修改的倒计时器
Hey there! Let's break down why your countdown code is crashing and get it working properly—plus add the ability to dynamically update the timer value whenever you need.
First, Why Your Current Code Is Crashing
Your while (timer > 1) loop runs synchronously on the main thread, and it doesn't wait for the postDelayed to finish before iterating again. That means in a split second, it creates hundreds of delayed tasks all scheduled to run 1 second later. This overwhelms the main thread, causes UI jank, and eventually leads to the crash you're seeing (the Signal Catcher log is a sign the system is trying to handle the overloaded thread).
On top of that, your global timer variable is being modified by all those delayed tasks at once, leading to inconsistent UI updates and race conditions.
Here's the Correct Implementation
The best way to handle countdowns in Android is using the built-in CountDownTimer class—it's designed for this exact use case, manages the timing correctly, and avoids thread issues.
Step 1: Declare a CountDownTimer Variable
First, add this variable to your Activity/Fragment (you can keep your global timer for sync if needed):
private var countDownTimer: CountDownTimer? = null var timer: Int = 50 // Keep your global variable if you need it elsewhere
Step 2: Create a Proper Countdown Function
Replace your existing timer() function with this:
private fun startCountdown(initialSeconds: Int) { // Cancel any existing timer first to avoid overlapping countdowns countDownTimer?.cancel() countDownTimer = object : CountDownTimer((initialSeconds * 1000).toLong(), 1000) { override fun onTick(millisUntilFinished: Long) { val remainingSeconds = (millisUntilFinished / 1000).toInt() timer_text.text = remainingSeconds.toString() // Sync the global timer variable if you need it elsewhere timer = remainingSeconds } override fun onFinish() { timer_text.text = "0" timer = 0 // Add any end-of-countdown logic here (e.g., show a completion message) } }.start() }
Step 3: Add Dynamic Update Support
To change the timer value on the fly (e.g., set it to 30 mid-countdown), just call this function:
fun updateCountdown(newSeconds: Int) { timer = newSeconds startCountdown(newSeconds) }
Step 4: Clean Up to Avoid Memory Leaks
Always cancel the timer when your Activity/Fragment is destroyed to prevent memory leaks:
override fun onDestroy() { super.onDestroy() countDownTimer?.cancel() }
How to Use It
- Start the initial countdown with
startCountdown(timer)(since your globaltimeris 50, this will start at 50) - Whenever you need to change the timer value, call
updateCountdown(30)(or any number you want)
Your Original Code & Logs for Reference
Global declaration:
var timer: Int = 50 // Declared globallyOriginal countdown function:
private fun timer(){ timer_text.text = timer.toString() // Text view in UI while (timer > 1){ Handler().postDelayed({ timer-- timer_text.text = timer.toString() // Update the Text view in UI }, 1000) } }Logcat output:
I/sandaru.projec: Thread[6,tid=11100,WaitingInMainSignalCatcherLoop,Thread*=0xb400007b3dcd9f50,peer=0x13280238,"Signal Catcher"]: reacting to signal 3 I/sandaru.projec: I/sandaru.projec: Wrote stack traces to tombstoned D/EGL_emulation: app_time_stats: avg=5.51ms min=1.98ms max=10.47ms count=60
内容的提问来源于stack exchange,提问作者Sandaru Fernando

