I have a timer which runs on the activity's OnCreate method as shown below. When run, the timer increments as it should. Showing:
00:00,
00:01,
00:02,
etc.
final Timer timer = new Timer();
final TimerTask timerTask = new TimerTask(){
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String timer = sdf.format(new Date(counter * 1000L));
timerText.setText(timer);
counter++;
}
});
}};
timer.schedule(timerTask, 0, 1000);
However, when I revisit the activity, the timer's interval increases. If I revisit it the first time, the interval becomes 2 i.e.
00:00,
00:02,
00:04,
etc.
Revisiting it again makes the interval 3 i.e.
00:00,
00:03,
00:06,
etc.
And the intervals keep incrementing.
I deduced the runOnUIThread method is being run n times, where n is the number of times onCreate has been accessed, but I don't really know what to do about it.
Use the Timer constructor that specifies it to be a daemon new Timer(true) so that if it exits the timer thread exits too.
Also consider cancelling the timer before exiting on onDestroy or onPause for example.
Related
I'm trying to start, end, and reset+restart a timer using two different if statements in the onDataChange method. The thing I can't get to work is getting the timer to restart if the first if statement is triggered again after the second one stops the timer. After the class declaration at the top, I have
Timer firstTestTimer = new Timer();
TimerTask increaseByTwo = new TimerTask() {
#Override
public void run() {
incrementalCount = incrementalCount + 2;
System.out.println(incrementalCount);
}
};
and in the onCreate method I have a Firebase reference with a ValueEventListener with the following code:
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String Motion1 = dataSnapshot.child("Enter").getValue().toString();
if(Motion1.equals("YES")){
incrementalCount = 0;
firstTestTimer.schedule(increaseByTwo, 2000,2000);
}
if(Motion1.equals("STOP")){
firstTestTimer.cancel();
firstTestTimer.purge();
System.out.println("Total seconds elapsed since sensor showed YES: "+incrementalCount);
if(incrementalCount > 11){
System.out.println("The difference is greater than 11 seconds");
}
}
}
As of right now, when the Firebase value initially changes to "YES", the timer starts and every two seconds it increases incrementalCount by 2. When the Firebase value changes to "STOP", the timer shows the correct amount of seconds that have passed. Here's the problem: When the Firebase value changes to "YES" again, the app crashes and gives me some variation of java.lang.IllegalStateException: Task already scheduled or cancelled
Is it possible to reset and restart the timer with a fresh count whenever the Firebase value changes to "YES"? Thank you!
As #ThomasKläger pointed out, you can't use a TimerTask again. The docs point out that:
A timer task is not reusable. Once a task has been scheduled for execution on a Timer or canceled, subsequent attempts to schedule it for execution will throw IllegalStateException.
It is like a plastic cup or spoon, built to be used only once. So every time you have to increment your timer, here's what you can do:
Leave the timer as it is and define the timer task object again:
if (Motion1.equals("YES")){
incrementalCount = 0;
//Define it again, I think it is a waste of time to check whether the task exists or not.
increaseByTwo = new TimerTask() {
#Override
public void run() {
incrementalCount = incrementalCount + 2;
System.out.println(incrementalCount);
}
};
firstTestTimer.schedule(increaseByTwo, 2000,2000);
}
The rest of the code is the same. This should work.
The documentation for TimerTask is here: TimerTask JDK 11
I have to show multiple clocks in screen from different places like New Delhi, Hong Kong, Frankfurt, Washington, etc. And these times are changing like any other real clock but as Time-Left to a fixed date-time and they are added to the screen at different moments as the user adds them. For example:
New Delhi 1d 4h 20 min 5s
Hong Kong 9h 2min 55s
Washington 22min 3s
...
I have a Class which makes all the calculations to get those times in that format. The problem comes when these times are shown on screen. How do I make them to update their time at the same time? So all the changes in the seconds are shown at the same time. I know it won't be theoretically at the same time, but the most close to it. This is the timer I am using:
Timer t = new Timer();
t.scheduleAtFixedRate(
new TimerTask()
{
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
label[id].setText(getTimeLeft(id));
}
});
}
},
0, // run first occurrence immediately
1000); // run every seconds
Also, some of them freeze eventually. Does any body knows why?
How do I make them to update their time at the same time? So all the changes in the seconds are shown at the same time. I know it won't be theoretically at the same time, but the most close to it. This is the timer I am using:
Instead of using separate Timers for each label, use a single Timer for ALL the labels
Timer t = new Timer();
t.scheduleAtFixedRate(
new TimerTask()
{
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
for (int id = 0; id < label.length; id++) {
label[id].setText(getTimeLeft(id));
}
}
});
}
},
0, // run first occurrence immediately
1000); // run every seconds
This will reduce the overhead of resources on you system (one timer instead of n times), possible event queue spamming as multiple timers trigger simultaneously and allow the times to "seem" to update at the same time, as they are all updated within the event queue, so they won't update until the next paint cycle, which won't happen until you exit the run block...
You could also make use Timeline, which would reduce the need for Platform.runLater, see How to update the label box every 2 seconds in java fx? for an example.
The count down does not work. I'm triggering it through a button.
public void startCountDown() {
timer.schedule(new TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable() {
public void run() {
countDown--;
countDownText.setText("Time left:" + countDown);
if (countDown < 0)
timer.cancel();
}
});
}
}, 1000); //Every 1 second
}
The countDown variable is set to 60, so the countdown starts at 60
EDIT: The countdown gets stuck at 59 seconds, countDown is an int. No errors. And countDownText is declared as text.
#FXML
private Text countDownText;
There's more than 1 timer class, but I assume you used java.util.Timer
Examining the API shows you used this method:
public void schedule(TimerTask task,
long delay)
Schedules the specified task for execution after the specified delay.
Parameters:
task - task to be scheduled.
delay - delay in milliseconds before task is to be executed.
Throws:
IllegalArgumentException - if delay is negative, or delay + System.currentTimeMillis() is negative.
IllegalStateException - if task was already scheduled or cancelled, timer was cancelled, or timer thread terminated.
NullPointerException - if task is null
So your program waits 1 second then executes the run() method, but does not repeat. To make it repeat, you need to use this method:
public void schedule(TimerTask task,
long delay,
long period)
Schedules the specified task for repeated fixed-delay execution, beginning after the specified delay. Subsequent executions take place at approximately regular intervals separated by the specified period.
In fixed-delay execution, each execution is scheduled relative to the actual execution time of the previous execution. If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well. In the long run, the frequency of execution will generally be slightly lower than the reciprocal of the specified period (assuming the system clock underlying Object.wait(long) is accurate).
Fixed-delay execution is appropriate for recurring activities that require "smoothness." In other words, it is appropriate for activities where it is more important to keep the frequency accurate in the short run than in the long run. This includes most animation tasks, such as blinking a cursor at regular intervals. It also includes tasks wherein regular activity is performed in response to human input, such as automatically repeating a character as long as a key is held down.
Parameters:
task - task to be scheduled.
delay - delay in milliseconds before task is to be executed.
period - time in milliseconds between successive task executions.
Throws:
IllegalArgumentException - if delay < 0, or delay + System.currentTimeMillis() < 0, or period <= 0
IllegalStateException - if task was already scheduled or cancelled, timer was cancelled, or timer thread terminated.
NullPointerException - if task is null
You can view the complete documentation here: http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html#schedule%28java.util.TimerTask,%20long%29
Here is what I think is correct code:
public void startCountDown() {
timer.schedule(new TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable() {
public void run() {
countDown--;
countDownText.setText("Time left:" + countDown);
if (countDown < 0)
timer.cancel();
}
});
}
}, 1000, 1000); //Every 1 second
}
I have an if statement which evaluates the time since the program has begun running and if the time is above a certain threshold, does something. I want this if statement to be checked throughout the whole time the program is running while at the same time have the program continue execution. How would I go about doing this?
Thank you.
The easiest approach would be to use a Timer. With that, you don't need the if logic; you can just use the firstTime argument when scheduling a TimerTask.
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
// do something
}
};
// schedule the task to be run every 100ms (0.1 sec),
// starting after "threshold" milliseconds have past
timer.schedule(task, threshold, 100);
It's not clear from your description if you want to repeatedly "do something" once the time threshold has been exceeded, or if you just want to wait until a certain time has passed and then "do something" once. The above code is for the repeating case. For a one-shot occurrence at some future time, change the last line to:
timer.schedule(task, threshold);
If you're using Swing, you should use a Swing Timer rather than a java.util.Timer. See How to Use Swing Timers for more info.
EDIT: Your comment clarified things a bit. It's fairly easy to do what you described:
Timer timer = new Timer();
TimerTask task = new TimerTask() {
private final long start = System.currentTimeMillis();
#Override
public void run() {
if (System.currentTimeMillis() - start < threshold) {
// do something
} else {
// do something else
}
}
};
// schedule the task to be run every 100ms (0.1 sec), starting immediately
timer.schedule(task, 0, 100);
Note that "do something" and "do something else" can be method calls to an enclosing class.
A cleaner approach might be to define several TimerTasks that are scheduled to execute at different times. The "something else" task that triggers an exception can be scheduled for one-time execution at the threshold time. You can also cancel individual tasks and you can even schedule a task that will cancel another task.
I have an app with multiple timers; when I start one, I can see it counting down in Logcat in Eclipse.
When I hit the Back button, onStop() is called in the app but the timer continues to countdown in logcat (the onTick() is continuing to tick away).
What I would like is when onResume() is called, I want to get that timer which is still counting down and continue it. Is this possible?
Here are my buttons that start the countdown:
//Button1 Timer
final CountDown button1 = new CountDown(420000,1000,bButton1);
bButton1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
button1.start();
long currentTime = System.currentTimeMillis();
//Set start time for calculating time elapsed if needed
saveTime("START_TIME_BUTTON1", currentTime);
}
});
//Button2 Timer
final CountDown button2 = new CountDown(360000,1000,bButton2);
bButton2.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
button2.start();
long currentTime = System.currentTimeMillis();
//Set start time for calculating time elapsed if needed
saveTime("START_TIME", currentTime);
}
});
My onResume() looks like this:
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
//Reset the timers on appropriate buttons if there was something running
//If nothing was running or timer finished - have button in default state
//See if there was a button1 timer running - if not, it will be 0
SharedPreferences start = PreferenceManager.getDefaultSharedPreferences(this);
long startTime = start.getLong("START_TIME_BUTTON1", 0);
long timeElapsed = (System.currentTimeMillis()-startTime);
long timeRemaining = (420000 - timeElapsed);
if (timeRemaining == 0) {
} else if (timeRemaining > 0) {
final CountDown button1Timer = new CountDown(timeRemaining,1000,bButton1);
button1Timer.start();
long currentTime = System.currentTimeMillis();
saveTime("START_TIME", currentTime);
} else {
}
}
This actually works — the onResume() starts another timer right where the first one would be, and the text displays the appropriate number and continues counting down with the onTick() method. But now logcat shows 2 timers counting down instead of just one!
Ultimately I wouldn't want to start another timer, I just want to pick up the first timer I started where it is currently in the countdown at and have the onTick() display appropriately. Is there a way to do that? Would services be what I'm looking for? Is it already a service since it continues to tick down in the background?
I'm a little confused on what would be best practice to get this done.
onTick() will continue being called until either the time remaining reaches 0 or you call cancel() on the CountDownTimer.
So in your onStop() method you will need to call cancel() on the timer you want to stop receiving onTick() notifications.
Android's implementation of CountDownTimer uses a Handler to perform timing by queuing a Message to be sent to the Handler after each tick. Unfortunately there is no way to pause the CountDownTimer, so I believe you will need to create a new Timer with the appropriate values like you're doing currently.
When you're calculating the timeElapsed, you shouldn't use System.currentTimeMillis() because it can be changed at any time, instead you should use SystemClock.elapsedRealtime(). System.currentTimeMillis() is the timer used for the wall clock (time and date) and so can change unpredictably, see the Android documentation.