I have an activity with a couple of TimerView
s. TimerView is a custom view extended from textview which the user can click to start a countdown.
Each TimerView owns a Timer. The problem right now is that when I change the orientation of the screen the TimerViews get reset (the current text is remove (changed to "") but the runtime changes to the bakground are kept), but the Handler threads are still running, updating TimerViews which no longer exist(?).
Messages which were sent just before the orientation change are still active. When will the threads(?) sending these messages terminate?
What is the correct way to solve this. Update the reference from the Timers to the new activity's TimerViews, or remove the Timers and create new ones? Or some other more correct solution? All constructive criticism is appreciated!
I'm posting the parts of the code I think is relevant:
TimerView extends TextView
public class TimerView extends TextView implements View.OnClickListener
{
private SecondTimer secondTimer;
public TimerView(Context context, AttributeSet attrs)
{
super(context, attrs);
secondTimer = new SecondTimer(120, this);
setOnClickListener(this);
}
@Override
public void onClick(View view)
{
if(!secondTimer.isRunning())
secondTimer.start();
}
}
SecondTimer
// Derived from Sam's timer here android CountDownTimer - additional milliseconds delay between ticks
public abstract class SecondTimer
{
private final long millisInFuture;
private final long countdownInterval;
private long stopTimeInFuture;
private long nextTime;
private boolean running;
// Reference to TimerView that this timer should update
TimerView timerView;
// Constructor
public SecondTimer(long secondsInFuture, TimerView timerView)
{
this.millisInFuture = secondsInFuture * 1000;
this.countdownInterval = 1000;
this.timerView = timerView;
}
private static final int MSG = 1;
public synchronized SecondTimer start() //Synch needed?
{
running = true;
if(millisInFuture <= 0)
{
onFinish();
return this;
}
nextTime = SystemClock.uptimeMillis();
stopTimeInFuture = nextTime + millisInFuture;
// Message for start tick
handler.sendMessage(handler.obtainMessage(MSG));
return this;
}
// Takes care of counting down
private Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
synchronized(SecondTimer.this)
{
final long millisLeft = stopTimeInFuture - SystemClock.uptimeMillis();
if(millisLeft <= 0)
{
onFinish();
}
else
{
onTick(millisLeft);
long currentTime = SystemClock.uptimeMillis();
do
{
nextTime += countdownInterval;
} while(currentTime > nextTime);
// Make sure this interval doesn't exceed the stop time
if(nextTime < stopTimeInFuture)
sendMessageAtTime(obtainMessage(MSG), nextTime);
else
sendMessageAtTime(obtainMessage(MSG), stopTimeInFuture);
}
}
}
};
private void onTick(long millisUntilFinished)
{
long secondsUntilFinished = (millisUntilFinished + 500) / 1000;
long minutesUntilFinished = secondsUntilFinished / 60;
secondsUntilFinished %= 60;
timerView.setText(String.format("%01d", minutesUntilFinished)
+ ":"
+ String.format("%02d", secondsUntilFinished));
}
private void onFinish()
{
running = false;
}
public boolean isRunning()
{
return running;
}
}