0

I've read well enough on the subject to get well and thoroughly confused. I'm doing a workout tracker app such as RunKeeper, which tracks how long and how far the user has been say, running. I'm in the process of programming the main interface of this "workout" which shows a stopwatch-like layout which includes both a timer showing the time which is updated every second, and a counter showing how far he's run so far.

My problem is updating the interface. I have a stopwatch class keeping track of the time, and I calculate the distance based on gps locations, but I'm at a loss to find the recommended way to run a continuous thread in the background that updates the ui on a fixed time rate.

As far as I've been able to tell, I should either be implementing the Handler.Callback interface in the activity class, or have a separate Callback object together with a Thread, but I'm sort of at a loss on how to get it all to work together. I both need to update the time shown that I get from my stopwatch class (simple start/stop time calculation, nothing thread-related), and the distance calculated based on the locations received in onLocationChanged().

Here's a stripped down version of my Activity code:

public class WorkoutActivity extends Activity implements OnClickListener, LocationListener
{
    private LocationManager locManager;
    private boolean workoutStarted = false;

    // The TextViews to update
    private TextView timeLabel;
    private TextView distanceLabel;

    private Stopwatch stopwatch;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        /*
            ... Interface initialisation
        */
        stopwatch = new Stopwatch();
    }

    private void startWorkout() {/* ...*/}
    private void stopWorkout()  {/* ...*/}
    private void pauseWorkout() {/* ...*/}
    private void resumeWorkout(){/* ...*/}
    public void onClick(View v) {/* ...*/}
    public void onLocationChanged(Location location){/* ...*/}
}

According to most answers I've read around here I should use a Handler object extending the handleMessage method, but nowadays (at least according to the warning in Lint) you have to make such objects static to avoid memory leaks. That however, makes it a bit strange to directly access and change the other private objects in the activity class from within the Handler. I suppose you could solve this by making the objects you need to affect parameters in a class extending Handler, but I dunno, it feels like a bit of a "hack" (nested class within WorkoutActivity):

static class TimeHandler extends Handler
{
    static final int MSG_START_TIMER = 1;
    static final int MSG_UPDATE_TIMER = 2;
    static final int MSG_STOP_TIMER = 3;

    private static final int REFRESH_RATE = 1000;

    private Stopwatch stopwatch;
    private TextView timeLabel;

    public TimeHandler(Stopwatch stopwatch, TextView label)
    {
        this.stopwatch = stopwatch;
        this.timeLabel = label;
    }

    @Override
    public void handleMessage(Message msg)
    {
        super.handleMessage(msg);

        switch(msg.what)
        {
            case MSG_START_TIMER:
                stopwatch.start();
                this.sendEmptyMessage(MSG_UPDATE_TIMER);
                break;
            case MSG_UPDATE_TIMER:
                timeLabel.setText(stopwatch.getElapsedTimeString());
                this.sendEmptyMessageDelayed(MSG_UPDATE_TIMER, REFRESH_RATE);
                break;
            case MSG_STOP_TIMER:
                this.removeMessages(MSG_UPDATE_TIMER);
                stopwatch.stop();
                timeLabel.setText(stopwatch.getElapsedTimeString());
                break;
        }           
    }

So, could anyone please explain the "standard" way of updating the UI from a continuous background thread? Oh, and sorry for the long question, just wanted to be thorough.

yzfr1
  • 202
  • 4
  • 12
  • 1
    Why are you not using [AsyncTask](http://developer.android.com/reference/android/os/AsyncTask.html)? Android has a great feature to handle background works without blocking Main UI Thread of application in which you can find such methods like, `publishProgress()` and `onProgressUpdate()` for updating UI periodically. And easy to implement.. – user370305 Aug 20 '12 at 13:03
  • Activity.runOnUiThread(Runnable) is a decent way to do this. I don't know that there is a 'standard' way, but I think runOnUiThread is a good fit for your needs – CSmith Aug 20 '12 at 13:04
  • "you have to make such objects static to avoid memory leaks" - the opposite is true, if you have no static you pretty much can't have memory leaks – zapl Aug 20 '12 at 13:05
  • user370305: I dunno, I thought ASyncTask was better suited for things like downloads or something taking a specific amount of time, and then exiting. Can you send a message to kill off an asynctask when the user has, for example, pressed pause or stop? zapl: Then why the Lint warning "This Handler class should be static or leaks might occur" when creating a non-static handler class? See this question for reference: http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler – yzfr1 Aug 20 '12 at 13:23
  • @user370305 according to the reference docs you just linked to it says "AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent". So I guess it wouldn't be that good of an idea to have an AsyncTask running for an hour+, dependnig on the length of the user's workout... – yzfr1 Aug 20 '12 at 14:17

2 Answers2

3

Use runOnUiThread().

Define a Runnable:

private Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        mTextViewOne.setText("10 miles!");
        mTextViewTwo.setText("40 minutes!");
    }
};

Inside your continuous thread:

runOnUiThread(mRunnable);
Wroclai
  • 26,835
  • 7
  • 76
  • 67
  • Thank you for your suggestion. How do you propose I implement the actual continuous thread though? Would it be best to simply extend Thread in a nested class, or should some sort of Handler be used? Sorry, I'm still a bit confused on exactly when it's a good idea to use the Handlers... – yzfr1 Aug 20 '12 at 14:39
  • @yzfr1: I would have a single, extended `Thread` class. When I use `Handler`s I usually use them in a scenario where I want to delay a task. – Wroclai Aug 20 '12 at 14:46
  • Well, I was thought I'd let it update only like once a second or so, since I guess constant updates would be a bit excessive, but I suppose putting a Thread.sleep(1000) in the thread class would do the job just as well. – yzfr1 Aug 20 '12 at 14:52
-1

USe the following thread in AsyncTask... I hope this will work for you ....

   Handler mHandler;
private final Runnable mRunnable = new Runnable() {

        public void run() {
            Call your AsyncTask Class;;;;
            mHandler.postDelayed(mRunnable, 1000);
        }

    };
G M Ramesh
  • 3,420
  • 9
  • 37
  • 53