I'm trying to create a simple Workout Timer for Android. The user creates a
WorkoutPlan containing info such as total duration, total rest time, etc, the app will display a timer that updates every second.
Pretty simple to begin with, but I'm trying to build the app as properly as possible, i.e. separation of concern, correct techniques, responsive UI, ensure the timer will be accurate, etc.
Here's a simplification of what I have so far:
WorkoutPlan, the user can load/save different workout plans to the timer
Code:
public class WorkoutPlan {
public long duration;
}
TimerThread, this class implements `Runnable`, it takes in a `WorkoutPlan` in the constructor, start/stop, shows elapsed time.
Code:
public class TimerThread implements Runnable {
public boolean running = false;
private long lastStartTime;
private long savedTime;
private WorkoutPlan plan;
private Handler handler;
public TimerThread(WorkoutPlan p, Handler h) {
plan = p;
handler = h;
}
public synchronized void start() {
lastStartTime = System.currentTimeMillis();
running = true;
}
public synchronized void pause() {
savedTime = elapsedTime();
lastStartTime = 0;
running = false;
}
public synchronized long elapsedTime() {
if (lastStartTime == 0) {
return savedTime;
} else {
return savedTime + (System.currentTimeMillis() - lastStartTime);
}
}
public synchronized String currTimeStr() {
//format elapsed time in seconds to hh:mm:ss format
long elapsed = elapsedTime() / 1000;
long h = elapsed / 3600;
long m = (elapsed % 3600) / 60;
long s = elapsed % 60;
if (h > 0) {
return String.format("%02d:%02d:%02d", h, m, s);
} else {
return String.format("%02d:%02d", m, s);
}
}
@Override
public void run() {
while (running) {
try {
handler.sendMessage(handler.obtainMessage());
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
TimerView, the custom view that displays the elapsed time
Code:
public class TimerView extends View {
private final Paint mBg;
private final Paint mText;
private WorkoutPlan plan;
private TimerThread timer;
private Thread thread;
private Handler handler = new TimerHandler();
public TimerView(Context context, AttributeSet attrs) {
super(context, attrs);
plan = new WorkoutPlan();
timer = new TimerThread(plan, handler);
thread = new Thread(timer);
mBg = new Paint();
mBg.setColor(getResources().getColor(R.color.bg_default));
mText = new Paint();
mText.setColor(getResources().getColor(R.color.text));
}
public void start() {
timer.start();
thread.start();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//draw current time and current round
canvas.drawText(timer.currTimeStr(), 0, 50, mText);
}
private class TimerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
invalidate();
}
}
}
Finally,
WorkoutTimer, the activity that starts it off
Code:
public class WorkoutTimer extends Activity {
private TimerView mTimer;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timer);
mTimer = (TimerView) findViewById(R.id.timer_view);
}
@Override
protected void onStart() {
super.onStart();
mTimer.start();
}
}
A couple of questions:
- Where's the best place to create the `WorkoutPlan` object? Currently it's done in `TimerView`. Should it be done in the `WorkoutTimer` activity instead? If yes, how do I pass the `WorkoutPlan` object to `TimerView`?
- The app works fine the first time, but crashes when I go to the home screen then back to the app the second time. What's causing it to crash? Is it the theading?
- Currently I'm using a `Handler` in a new `Thread`. Is this ok or would a `TimerTask` be better? Where's the best place to start the thread?
- Am I correct to put all the threading code in `TimerView`? It feels like they should be somewhere else.
- Am I correct to add synchronized to all the methods in `TimerThread`?
- I'm trying to create this app as properly as I can for practice. Please let me know if there's any improvements in technique I should make, or if I'm doing something incorrectly.