Improve accuracy of my timer thread - java

Below I have a thread that updates a 'clock' label once per second. Once the thread reaches 600 (10 mins) the timer stops. The thread seems to trail by approx five seconds every minute. So when val = 60, in actuality 65 seconds may have elapsed. I'm using midp so I dont think I can introduce any api's to help me with this task. How can I improve on the accuracy below class? I think whats slowing it down is the method convertValToTimerString, is there a better way of converting the current timer val to minutes:seconds format without using java formatter apis?
Thanks,
public class LabelTimerUpdaterThread implements Runnable{
public static int val = 0;
private int minuteValueInt = 0;
private int secondValueInt = 0;
private int tempSecondValueInt;
public boolean isRunning = true;
public LabelTimerUpdaterThread(){
}
public void run() {
while(isRunning){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
val += 1;
incrementTimer();
if(val == 600){
break;
}
}
}
private void incrementTimer(){
Screen.timerLabel.setText(convertValToTimerString(val));
}
private String convertValToTimerString(int val){
String minuteValString = "";
String secondValString = "";
if(val < 10){
minuteValString = "00";
secondValString = "0"+String.valueOf(val);
return minuteValString+":"+secondValString;
}
if(val < 60){
minuteValString = "00";
secondValString = String.valueOf(val);
return minuteValString+":"+secondValString;
}
if(val % 60 == 0){
++minuteValueInt;
}
if(minuteValueInt < 10){
minuteValString = "0"+String.valueOf(minuteValueInt);
int secondVal = val % 60;
if(secondVal < 10){
return minuteValString+":0"+String.valueOf(secondVal);
}
else {
return minuteValString+":"+String.valueOf(secondVal);
}
}
return "10:00";
}
public void stopThread(){
this.isRunning = false;
}
Ok, now I am receiving an IllegalStateException when I try to upedate my timer label -
Here is my code -
Here i am instantiating my label -
timerLabel = new CustomLabelField("00:00" , Field.FIELD_LEFT , Constants.SMALL_FONT , Color.BLACK, Bitmap.getBitmapResource("bg_clock_white.png"));
UpdateValTimer timer = new UpdateValTimer(th);
timer.startTimer();
This class creates the timer class and creates the class which will update the timer label.
public class UpdateValTimer {
private Timer timer;
private int val = 0;
private UpdateView uv;
private final CustomLabelField customLabelField;
public UpdateValTimer(CustomLabelField field) {
this.customLabelField = field;
}
public void startTimer(){
timer = new Timer();
uv = new UpdateView(customLabelField);
Thread t = new Thread(uv);
t.start();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
++val;
}
}, 1000, 1000);
}
}
This class updates the timer label -
public class UpdateView implements Runnable{
private int val = 0;
private int minuteValueInt = 0;
private final CustomLabelField timerLabel;
public UpdateView(CustomLabelField timerLabel) {
this.timerLabel = timerLabel;
}
public void run() {
while(true){
this.timerLabel.setText(convertValToTimerString(this.val));
}
}
private String convertValToTimerString(int val){
String minuteValString = "";
String secondValString = "";
if(val < 10){
minuteValString = "00";
secondValString = "0"+String.valueOf(val);
return minuteValString+":"+secondValString;
}
if(val < 60){
minuteValString = "00";
secondValString = String.valueOf(val);
return minuteValString+":"+secondValString;
}
if(val % 60 == 0){
++minuteValueInt;
}
if(minuteValueInt < 10){
minuteValString = "0"+String.valueOf(minuteValueInt);
int secondVal = val % 60;
if(secondVal < 10){
return minuteValString+":0"+String.valueOf(secondVal);
}
else {
return minuteValString+":"+String.valueOf(secondVal);
}
}
return "10:00";
}
}
Thanks for any help
After some initial testing this code seems to be working correctly.
Thanks for all your help everyone.
Any comments on what I could be doing better are welcome.
public class UpdateValTimer{
private int minuteValueInt = 0;
private Timer timer;
private int val = 0;
private UpdateView uv;
private CustomLabelField customLabelField;
public UpdateValTimer(CustomLabelField field) {
this.customLabelField = field;
}
public void startTimer(){
timer = new Timer();
uv = new UpdateView(customLabelField);
Thread t = new Thread(uv);
t.start();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
++val;
uv.setVal(convertValToTimerString(val));
}
}, 1000, 1000);
}
private String convertValToTimerString(int val){
String minuteValString = "";
String secondValString = "";
if(val < 10){
minuteValString = "00";
secondValString = "0"+String.valueOf(val);
return minuteValString+":"+secondValString;
}
if(val < 60){
minuteValString = "00";
secondValString = String.valueOf(val);
return minuteValString+":"+secondValString;
}
if(val % 60 == 0){
++minuteValueInt;
}
if(minuteValueInt < 10){
minuteValString = "0"+String.valueOf(minuteValueInt);
int secondVal = val % 60;
if(secondVal < 10){
return minuteValString+":0"+String.valueOf(secondVal);
}
else {
return minuteValString+":"+String.valueOf(secondVal);
}
}
return "10:00";
}
}
public class UpdateView implements Runnable{
private String timeElapsedCounter;
private final CustomLabelField timerLabel;
public UpdateView(CustomLabelField timerLabel) {
this.timerLabel = timerLabel;
}
public void setVal(String timeElapsedCounter){
this.timeElapsedCounter = timeElapsedCounter;
}
public void run() {
while(true){
synchronized(Application.getEventLock()){
timerLabel.setText(this.timeElapsedCounter);
}
}
}
}
timerLabel = new CustomLabelField("00:00" , Field.FIELD_LEFT , Constants.SMALL_FONT , Color.BLACK, Bitmap.getBitmapResource("bg_clock_white.png"));
UpdateValTimer timer = new UpdateValTimer(timerLabel);
timer.startTimer();

Thread.sleep(1000);
Will put the thread into the sleeping state for at least 1s and after that it gets back to ready-to-run state which doesn't guarantee it will run immediatelly, it is just "ready to be scheduled for running anytime in the future". It is up to the thread scheduler when it will run it. You may want for example to check the system time to really know how much time elapsed ...

Try using ScheduledExecutorService.scheduleAtFixedRate which will execute your task at fixed intervals, regardless of how long the previous task took to complete.

If you want a timer, suggest you use a Timer. Also suggest that if you want to maintain a clock, you not attempt to do this yourself, but instead sample the system clock. Perhaps what you want is to sample currentTimeMillis()

There are two things that might help... First of all, the function incrementTimer() calls convertValToTimerString(). As you mentioned, this will introduce some slowdown. It will not be much, but over time this delay is going to accumulate. You're staying within the current thread of execution. A solution would be to use a model-view-controller scheme. The model would be your "val" field. The view is the label. The controller would then be the thread that updates this field once every second. By separating the model updates from the code execution required for viewing them, the update thread can run undisturbed. Of course, you'll still need a way to regularly update the text on the label based on your model's value. A separate thread could take care of this.
So what we have is:
A model (in your case, simply one value)
A controller (non-interactive thread updating the model)
A view (label)
A thread that updates the view
For the controller, using a Timer would indeed be a better choice rather than using a Runnable that calls Thread.sleep(), like andersoj suggested. You'll need to implement TimerTask. There is a difference between scheduling at fixed delay and scheduling at fixed rate. A fixed rate is preferrable for a task such as yours, where average consistency is more important than regularity. Do mind that if you use a Timer, the above model-view-controller scheme I suggested is a bit overkill. You'll probably be capable of incorporating the label update into the TimerTask's run method. But only because it will occur fast enough. If your view update requires more time (as might be the case for a combination of fast updates with heavy drawing) the MVC pattern will provide the proper separation of concerns and keep things spiffy.

Related

Coding an interval timer for a little workout program (just like an app) How can I get 2 timers (for a workout and pause interval) without redundancy?

I know how to count down using Timer and Tasktimer but im not sure how to reset my timer let alone adding another interval for my pause section without just copying my written code and creating another object for just my pause section. I wanted to save my int secondsworkout in another int so that I got my number (in seconds) and just reuse it somehow (if even possible).
public class Intervall {
Scanner stdin = new Scanner(System.in);
int secondsWorkout = stdin.nextInt() + 1;
int cache = secondsWorkout - 1;
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
secondsWorkout--;
System.out.println(secondsWorkout + " seconds");
if(secondsWorkout == 0) {
timer.cancel();
System.out.println("DING!");
secondsWorkout = cache;
}
}
};
public void startWorkout() {
timer.scheduleAtFixedRate(task, 1000, 1000);
}
public static void main(String[] args){
System.out.println("Please enter a number");
Intervall kek = new Intervall();
kek.startWorkout();
}
}
You are using an anonymous inner class which accesses fields of its outer class:
int secondsWorkout = stdin.nextInt() + 1;
int cache = secondsWorkout - 1;
TimerTask task = new TimerTask() {
#Override
public void run() {
secondsWorkout--;
...
If you want to make it reusable, declare a real class with fields:
class WorkoutTimerTask extends TimerTask {
int secondsWorkout;
int cache;
WorkoutTimerTask(int secondsWorkout, int cache) {
this.secondsWorkout = secondsWorkout;
this.cache = cache;
}
#Override
public void run() {
secondsWorkout--;
System.out.println(secondsWorkout + " seconds");
if(secondsWorkout == 0) {
timer.cancel();
System.out.println("DING!");
secondsWorkout = cache;
}
}
};
And then create two instances of it, e.g.:
Scanner stdin = new Scanner(System.in);
int secondsWorkout = stdin.nextInt() + 1;
int secondsPause = stdin.nextInt() + 1;
Timer timer = new Timer();
WorkoutTimerTask workout = new WorkoutTimerTask(secondsWorkout, secondsWorkout - 1);
WorkoutTimerTask pause = new WorkoutTimerTask(secondsPause, secondsPause - 1);
...
Notes:
You can declare the class as an inner class (in the same java file) or in a separate file called 'WorkoutTimerTask.java'.
Instead of counting down a second on each scheduled execution, you could also keep track of the System.currentTimeMillis() value at the start, and compare it to the current time millis. It's probably more accurate and maybe more understandable:
public class WorkoutTimerTask extends TimerTask {
private final int maxSeconds;
private long startMillis;
public WorkoutTimerTask(int maxSeconds) {
this.maxSeconds = maxSeconds;
this.startMillis = System.currentTimeMillis();
}
#Override
public void run() {
long elapsedSeconds = (System.currentTimeMillis() - startMillis) / 1000;
if (elapsedSeconds < maxSeconds) {
System.out.printf("%s seconds%n", elapsedSeconds);
} else {
System.out.println("DING!");
cancel();
}
}
};
You can also cancel the timer via the TimerTask by calling cancel()
EDIT: Thinking about what you're trying to do, I think maybe you don't want to try and 'chain' these TimerTasks at all. Instead, just implement a smart TimerTask which knows about workout and pause phases, for example:
public class WorkoutTimer extends TimerTask {
private final int workoutSeconds;
private final int pauseSeconds;
private long startMillis;
private Phase phase;
// bonus feature: keep track of the number of workout/pause cycles
private int workoutCounter;
private enum Phase {
WORKOUT, PAUSE
}
public WorkoutTimer(int workoutSeconds, int pauseSeconds) {
this.workoutSeconds = workoutSeconds;
this.pauseSeconds = pauseSeconds;
}
#Override
public void run() {
if (phase == null) {
startNextPhase();
}
long elapsedSeconds = getElapsedSeconds();
long maxSeconds = getMaxSeconds();
if (elapsedSeconds < maxSeconds) {
// carriage return at the end allows 'printing in place'
System.out.printf("%s#%s %s seconds\r", phase, workoutCounter, elapsedSeconds + 1);
}
else {
System.out.printf("%s#%s finished%n", phase, workoutCounter);
startNextPhase();
}
}
private long getElapsedSeconds() {
return (System.currentTimeMillis() - startMillis) / 1000;
}
private long getMaxSeconds() {
if (phase == Phase.WORKOUT) {
return workoutSeconds;
}
else {
return pauseSeconds;
}
}
private void startNextPhase() {
if (phase == null || phase == Phase.PAUSE) {
phase = Phase.WORKOUT;
workoutCounter++;
}
else {
phase = Phase.PAUSE;
}
startMillis = System.currentTimeMillis();
}
public static void main(String[] args) {
Scanner stdin = new Scanner(System.in);
System.out.println("Please enter the number of workout seconds");
int workoutSeconds = stdin.nextInt();
System.out.println("Please enter the number of pause seconds");
int pauseSeconds = stdin.nextInt();
WorkoutTimer workoutTimer = new WorkoutTimer(workoutSeconds, pauseSeconds);
Timer timer = new Timer();
// 300 to make sure the timer doesn't seem laggy
timer.scheduleAtFixedRate(workoutTimer, 0, 300);
}
}

Handler.postDelayed causes lag

I created an application that can call using our SIP server. Now I want to do is to show the user how long his call has been on going. My script is actually working but I noticed that the longer the call, the laggy my app becomes. Here's the snippet of my code
Handler h = new Handler(Looper.getMainLooper());
_isOnCall = true;
long time = 0;
int x = 0;
while(_isOnCall) {
if (_isOnCall){
final int counter = 1 + x;
time += 1000;
h.postDelayed(new Runnable() {
#Override
public void run() {
(TargetDetailsActivity.this).runOnUiThread(new Runnable() {
#Override
public void run() {
final int seconds = counter % 60;
final int minutes = (counter % 3600) / 60;
final int hours = counter / 3600;
callcounter.post(new Runnable() {
#Override
public void run() {
callcounter.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
});
}
});
}
}, time);
x++;
}else{
break;
}
}
Basically what the code does is just to count the seconds/minutes/hour he's been on the phone. When he hangs up, I call the code below:
_isOnCall = false;
h.removeCallbacksAndMessages(null);
I'm not sure what causes the lag. Help! Thanks.
UPDATE
I was able to make this working by utilizing galvan's suggestion using Timer. Here's my code for future reference:
private Timer myTimer;
private int counter_time=0;
public void onCallEstablished() {
myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
TimerMethod();
}
}, 0, 1000);
}
private void TimerMethod()
{
this.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable() {
public void run() {
counter_time++;
int seconds = counter_time % 60;
int minutes = (counter_time % 3600) / 60;
int hours = counter_time / 3600;
callcounter.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
};
public void releaseCall(){
if(myTimer != null){
myTimer.cancel();
myTimer.purge();
counter_time = 0;
}
}
It looks like every Runnable saves a reference to his parent object, a nested Runnable in this case. Try to take an heap snapshot for detecting a memory issues to see if this is the case here.
You can also make a repeat task with time interval, and stop the loop when the call ends.
Timer timer = new Timer();
TimerTask myTask = new TimerTask() {
#Override
public void run() {
// whatever you need to do every 2 seconds
}
};
timer.scheduleAtFixedRate(myTask,
firstTime,
period);
for more info about timer you can take a look at the docs

How can I make Java add to a private int mouseClicks every second?

public class Blank extends WindowController
{
private int mouseClicks;
public void onMousePress(Location point)
{
mouseClicks++;
}
}
My goal is for if mouseClicks to increment once every second, while while only having to click once to start it.
Here is the best solution I can get.
public class Blank extends WindowController
{
private final AtomicInteger mouseClicks = new AtomicInteger();
private boolean hasStarted = false;
public void onMousePress(Location point)
{
if(!hasStarted){
hasStarted = true;
Thread t = new Thread(){
public void run(){
while(true){
mouseClicks.incrementAndGet(); //adds one to integer
Thread.sleep(1000); //surround with try and catch
}
}
};
t.start();
}
}
}
Look into using Thread.sleep(1000); to pause execution for one second.
http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
You could test the time passed since the last click.
long lLastClickTime = Long.MIN_VALUE;
public void onMousePress(Location point) {
final long lCurrentTime = System.currentTimeMillis();
if(lCurrentTime - lClickLastTime >= 1000) {
mouseClicks++;
lLastClickTime = lCurrentTime;
}
}

multifunction stopWatch class in Java

I'm writing my first project in java, and I could use some help and experienced view. I'm trying to write stopWatch class, or something more like kitchen timer, which can provide this functionality:
- set hard start and end times,
- pause and start time from same place
- set if the time is decreasing or increasing
- move in Time up and down
- addTime (example: watch suppose to start at 00:00 end in 30:00 if we add 35 seconds stopWatch will end at 30:35);
- display time every second
I didn't find any good thread about this kind of stopWatch/kitchen timer here on stack overflow. My code is working, but I don't really have much of experience with OOD and java generally, so I'm expecting some flaws. It would be reasonable to use some pre-written classes instead? Or Date for time operation?
My code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
// class for handle timer operation
public class stopWatch implements ActionListener {
public Timer timer; // timer object
private int startTime; // start time in seconds
private int endTime; // end time in seconds
private int actualTime; // actual time in seconds
private long memoryUnixTime; // actual time in Unix format
private boolean decrease; // is Timer decrease or incerease Time?
private int presize; // how often timer check for change of time
// construct
public stopWatch(int startTime, int endTime, boolean decrease, int presize){
this.startTime = startTime;
this.endTime = endTime;
this.decrease = decrease;
this.presize = presize;
this.actualTime = startTime;
}
// moving in Time UP
public void moveTimeUp (int seconds){
if(inScope("up",seconds))
actualTime += seconds;
}
// moving in Time DOWN
public void moveTimeDown (int seconds){
if(inScope("down",seconds))
actualTime -= seconds;
}
// is added time still inScope
private boolean inScope(String direction,int seconds){
boolean inScopeP;
switch(direction){
case "up":
inScopeP = ((this.actualTime + seconds) <= this.endTime);
break;
case "down":
inScopeP = ((this.actualTime - seconds) >= this.startTime);
break;
default:
inScopeP = false;
break;
}
return inScopeP;
}
// addTime - this option will be used only if we use increasing timer
public void addTime(int seconds){
if(this.decrease == false)
this.endTime += seconds;
}
// run stopwatch
public void run(){
System.out.println("start");
this.timer = new Timer(this.presize, this);
this.timer.setRepeats(true);
this.timer.start();
}
//stop stopwatch
public void pause(){
this.timer.stop();
this.timer = null;
}
// end of counting
public void end(){
System.out.println("END");
this.pause();
}
// event listener, handle change time
#Override
public void actionPerformed(ActionEvent e) {
// If the timer caused this event.
if (e.getSource().equals(timer)) {
long currentTime = System.currentTimeMillis();
// If time had change we decrease or increase time and print it out
if(changeTimeP(currentTime)){
// if time`s up we end the counting
if(this.actualTime == this.endTime){
this.end();
// else we do the math increase
} else{
this.memoryUnixTime = currentTime/1000;
if(decrease == true)
{
this.actualTime--;
} else {
this.actualTime++;
}
// println into console for testing purposes
int min = this.actualTime / 60;
int sec = this.actualTime % 60;
System.out.println(min+":"+sec);
}
}
}
}
// did time changed? (in matter of one second)
private boolean changeTimeP(long currentTime){
if(this.memoryUnixTime != (currentTime/1000L))
return true;
else return false;
}
}
Peter

TimerTask can't update the JavaFX Label text

I have a fairly simple JavaFX GUI application which has an label that shows how much time is left until a certain action starts. To achieve this, I've created a DownloadTimer class as shown below:
public class DownloadTimer(){
private int minutes;
private int seconds;
private Timer innerTimer = new Timer();
private TimerTask innerTask;
private boolean isActive;
public DownloadTimer(int minutes, int seconds) {
if (seconds > 60) {
int minToAdd = seconds / 60;
this.minutes = minutes;
this.minutes += minToAdd;
this.seconds = seconds % 60;
} else {
this.minutes = minutes;
this.seconds = seconds;
}
}
public void start() {
innerTask = new TimerTask() {
#Override
public void run() {
isActive = true;
System.out.println(getTime());
if (seconds == 0 && minutes > 0){
minutes -= 1;
seconds = 59;
} else if (seconds == 0 && minutes == 0){
isActive = false;
innerTimer.cancel();
innerTimer.purge();
System.out.println("DownloadTimer DONE");
} else {
seconds -= 1;
}
}
};
innerTimer.scheduleAtFixedRate(innerTask, 0, 1000);
}
}
And then, I'm creating the DownloadTimer object and starting the countdown from my Main (JavaFX) class:
/*
code omitted for better readability
*/
downloadTimer = new DownloadTimer(0, 5);
// label gets the .getTime() value, which returns a formatted String like "00:05", "00:04", etc.
lblTimer.setText( downloadTimer.getTime() );
// start the countdown
downloadTimer.start();
// create a new timer which checks if the downloadTimer is still counting
final Timer timer = new Timer();
TimerTask timerTask = new TimerTask(){
#Override
public void run(){
if (downloadTimer.getIsActive() == false){
timer.cancel();
timer.purge();
System.out.println("GUI timer DONE");
} else {
// if it's still running, then continuously update the label's text
lblTimer.setText( downloadTimer.getTime() );
// this is where I get the error described below
}
}
};
// repeat after 1000ms
timer.scheduleAtFixedRate(timerTask, 0, 1000);
The problem I'm encountering with this is that I can't set the label text with lblTimer.setText( downloadTimer.getTime() ); from the Main class, and the error I'm getting is TimerThread.run() line: not available [local variables unavailable] as seen here.
I've read about ScheduledThreadPoolExecutor and Java Timer vs ExecutorService, but I'm curious if this can be done using two separate Timers and TimerTasks. Any help and/or tips would be greatly appreciated.
I'm surprised you're not seeing an exception. To update a label from a separate thread, one needs to schedule an update to be run in the FX thread:
Platform.runLater(new Runnable() {
public void run() {
lblTimer.setText(downloadTimer.getTime());
}
});

Categories