I am writing a game app that uses a ChessClock class. Each of the two players has a clock. The clocks send themselves a stream of messages every tenth of a second, and if running, decrement the time remaining. When the time expires the clock calls a method in the parent view group that stops the clocks and sets a flag that causes further touch events to be ignored so that the users can no longer move the pieces. I am writing in kotlin, but I think this will be clear to a java programmer also:
class ChessClock : TextView {
constructor(context: Context) : super(context)
constructor(context:Context, p:String) : this(context) {
player = p
}
var player = ""
val RUNNING = 0
val PAUSED = 1
val IDLE = 2
val EXPIRED = 3
val mInterval = 100L
var mTime = 0L // in milliseconds
var mState = IDLE
val mHandler = Handler()
val mUpdateTimeTask = object : Runnable {
override fun run() {
if (mState == RUNNING) showTime()
mHandler.postDelayed(this, mInterval)
}
}
// override fun onDetachedFromWindow() {
// super.onDetachedFromWindow()
// stop()
// }
fun setTime(minutes:Long) {
mTime = 1000*60*minutes // minutes to milliseconds
mState = IDLE
setTextColor(getResources().getColor(R.color.colorIdle))
showTime()
mUpdateTimeTask.run()
}
fun pause() {
mState = PAUSED
setTextColor(getResources().getColor(R.color.colorPaused))
}
fun start() {
mState = RUNNING
setTextColor(getResources().getColor(R.color.colorRunning))
}
fun expire() {
mState = EXPIRED
setTextColor(getResources().getColor(R.color.colorExpired))
model.timeout(player)
val aboyne = parent as AboyneView
aboyne.gameOver(false)
}
fun toggle() {
if (mState == PAUSED) start()
else if (mState == RUNNING) pause()
}
fun stop() {
mHandler.removeCallbacks(mUpdateTimeTask)
}
fun showTime() {
val tenths = (mTime % 1000) / 100
var seconds = mTime / 1000
val minutes = seconds % 3600 / 60
seconds %= 60
if (minutes >= 1)
setText(String.format("%02d:%02d",minutes, seconds))
else
setText(String.format("%01d:%02d:%01d",minutes, seconds,
tenths))
if (mState != RUNNING) return
if (mTime > 0)
mTime -= mInterval
else
expire()
}
}
This works fine, but if the user presses the back button and then starts a new game, the clocks apparently keep running, even though the activity's onDestroy method has been called. Then when the running clock expires, the new game is frozen even though the new clocks are still running.
I'm new to android and I don't understand how this comes about. I would have thought that the clocks would be destroyed when the activity was. I've tested my ideas about what is happening with log messages, and have confirmed that the clocks run after onDestroy are called. I've also overridden onDetachedFromWindow to stop the clocks, and it makes the problem go away, but this is overkill. It will also stop the clock if the user gets a phone call, won't it, and this is not what I want.
Can you suggest how to best accomplish what I want? There is no way to access the clocks from the activity is there? I've thought of emitting some event in onDestroy, and having the ChessClock listen for it, but I don't know how to write custom events and listeners yet. Is there an easier way?