0

I have an activity with a couple of TimerViews. 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;
    }
}
Community
  • 1
  • 1
Moberg
  • 5,253
  • 4
  • 38
  • 54

1 Answers1

0

One approach would be to move your timers to a setRetainInstance(true) fragment. Such a fragment will not be destroyed on an orientation change. ( btw It is best NOT to update the UI in a retained fragment.)

IanB
  • 3,489
  • 1
  • 20
  • 24
  • So how can I have them update the UI if they are in a Retained fragment? – Moberg Aug 06 '13 at 20:36
  • I recommend this: http://www.vogella.com/articles/AndroidFragments/article.html section 4.2 is particularly relevant. Update the UI in another fragment or the parent activity. – IanB Aug 06 '13 at 22:07