52

I'm using a timer to create a stop watch. The timer works by increasing a integer value. I want to then display this value in the activity by constantly updating a textview.

Here's my code from the service where I try and update the activity's textview:

protected static void startTimer() {
    isTimerRunning = true; 
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            elapsedTime += 1; //increase every sec
            StopWatch.time.setText(formatIntoHHMMSS(elapsedTime)); //this is the textview
        }
    }, 0, 1000);
}

I got some kind of error about updating the UI in the wrong thread.

How can I adapt my code to accomplish this task of constantly updating the textview?

A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128
JDS
  • 16,388
  • 47
  • 161
  • 224
  • im not familiar with the andriod api but you might have to manually call repaint() or whatever method u use when you update the textfield constanlty. – Stas Jaro Jul 14 '11 at 22:41

9 Answers9

92
protected static void startTimer() {
    isTimerRunning = true; 
    timer.scheduleAtFixedRate(new TimerTask() {
        public void run() {
            elapsedTime += 1; //increase every sec
            mHandler.obtainMessage(1).sendToTarget();
        }
    }, 0, 1000);
}

public Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        StopWatch.time.setText(formatIntoHHMMSS(elapsedTime)); //this is the textview
    }
};

Above code will work...

Note: Handlers must be created in your main thread so that you can modify UI content.

VishalKale
  • 758
  • 7
  • 22
Nirav
  • 5,700
  • 4
  • 34
  • 44
  • Nice simple solution. One correction to make though -- the final brace "}" completing your handler definition requires a ";" – Art Geigel May 14 '13 at 03:56
  • 7
    @Nirav you might want to note for people that the package for Handler is android.os and not one of the others. – gregtzar May 04 '14 at 03:44
  • The semi colon is supposed to be after the hander assignment, not the where it now is. – matt Jul 24 '14 at 19:58
  • Note that "schedule" is useful alternative to "scheduleAtFixedRate", for cases where instead of exact time, you want some task to be repeated after a given delay. The difference is that if one run is delayed (beyond its requested time) by say 1/2 sec due to other activity, the next run will be the specified time after that DELAYED start. I use this when there is something I want to check occasionally, but if there ever happens to be a delay, there is no point in checking again quickly. – ToolmakerSteve Sep 12 '14 at 16:03
  • i think any code on handler need to implement the onresume and onstop methods to safely start and stop handler/timer – vin shaba Oct 12 '20 at 09:16
8

You should use Handler instead to update UI every X seconds. Here is another question that show an example: Repeat a task with a time delay?

Your approach doesn't work because you are trying to update UI from non-UI thread. This is not allowed.

Community
  • 1
  • 1
inazaruk
  • 74,247
  • 24
  • 188
  • 156
6
StopWatch.time.post(new Runnable() {
    StopWatch.time.setText(formatIntoHHMMSS(elapsedTime));
});

this code block is based on Handler but you don't need to create your own Handler instance.

DongXu
  • 1,525
  • 14
  • 12
  • 35
    Note to future readers: There is no built-in class "StopWatch" in Android; this answer is referring to OP's code example, in which "StopWatch" is the name of the OP's TextView. – ToolmakerSteve Sep 12 '14 at 16:11
2

You can use the following utility :

/**
 * Created by Ofek on 19/08/2015.
 */
public class TaskScheduler extends Handler {
    private ArrayMap<Runnable,Runnable> tasks = new ArrayMap<>();
    public void scheduleAtFixedRate(final Runnable task,long delay,final long period) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.run();
                postDelayed(this, period);
            }
        };
        tasks.put(task, runnable);
        postDelayed(runnable, delay);
    }
    public void scheduleAtFixedRate(final Runnable task,final long period) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                task.run();
                postDelayed(this, period);
            }
        };
        tasks.put(task, runnable);
        runnable.run();
    }
    public void stop(Runnable task) {
        Runnable removed = tasks.remove(task);
        if (removed!=null) removeCallbacks(removed);
    }

}

Then anywhere in code that runs by the UI Thread you can use it simply like this:

TaskScheduler timer = new TaskScheduler();
        timer.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                time.setText(simpleDateFormat.format(GamePlay.instance().getLevelTime()));
            }
        },1000);
Ofek Ron
  • 8,354
  • 13
  • 55
  • 103
2

you can use Handler.

this code increase a counter every one second and show and update counter value on a textView.

public class MainActivity extends Activity {


    private Handler handler = new Handler();
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        startTimer();
    }


    int i = 0;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            i++;
            textView.setText("counter:" + i);
            startTimer();
        }
    };

    public void startTimer() {
        handler.postDelayed(runnable, 1000);
    }

    public void cancelTimer() {
        handler.removeCallbacks(runnable);
    }

    @Override
    protected void onStop() {
        super.onStop();
        handler.removeCallbacks(runnable);
    }
}
faraz khonsari
  • 1,924
  • 1
  • 19
  • 27
2

TimerTask implements Runnable, which would make it a thread. You can not update the main UI thread directly from a different thread without some work. One thing you could do is use Async Task to create the timer and publish an update every second that will update the UI.

Ashterothi
  • 3,282
  • 1
  • 21
  • 35
2

I'm assuming StopWatch.time is some static or public reference to your TextView. Instead of doing this, you should implement a BroadcastReceiver to communicate between your timer (which runs from a separate thread) and your TextView.

John Leehey
  • 22,052
  • 8
  • 61
  • 88
-1

I use this way:

String[] array = {"man", "for", "think"}; 
int j;

Then add more onCreate:

TextView t = findViewById(R.id.textView);

new CountDownTimer(5000,1000) {

    @Override
    public void onTick(long millisUntilFinished) {}

    @Override
    public void onFinish() {
        t.setText("I " + array[j] + " You");
        j++;
        if (j == array.length - 1) j = 0;
        start();
    }
}.start();
Vukašin Manojlović
  • 3,717
  • 3
  • 19
  • 31
Mori
  • 2,653
  • 18
  • 24
-3
 timer.schedule(new TimerTask() {
            @Override
            public void run() {

                //your actions

            }
        },1*1000);//1 sec