I'm using a runnable in my Android app to update a countdown timer, as shown in the code below. It appears to work but I noticed my timer takes a few seconds longer than expected. For example, if it's supposed to count down 3 minutes, it takes 3 minutes and 5 seconds. I tried using a timer in a service to manage the countdown display in the main activity. The timer/service worked as expected.
Why doesn't runnable/postDelayed() run for the correct amount of time? Is postDelayed() timing reliable? The runnable decrements a variable then uses it to update an EditText with setText(). Does setText() take too long (a small fraction of a second), so the runnable really runs every 1.x seconds?
Handler handler = new Handler();
Runnable r = new Runnable() {
public void run() {
// decrement the time remaining and update the display
handler.postDelayed(this, 1000);
}
};
...
// start the runnable
handler.postDelayed(r, 1000);
Your code is kinda sorta designed to be inaccurate because you are not accounting for the time taken by the guts of the runnable. You might get improved results by doing something like
public void run(){
startTime = System.currentTimeMillis();
// compare expectedTime to startTime and compensate
// <guts of runnable goes here>
// now wrap it up...
delay = 1000 - (System.currentTimeMillis() - startTime);
if (delay < 0)
delay = 0;
expectedTime = System.currentTimeMillies() + delay;
handler.postDelayed(this, delay);
}
What about using CountDownTimer? I used this for same tasks several times and haven’t met this kind of problem.
Related
Basically, I am trying to do something more complicated than that, but this is my problem:
When using handler.postDelayed inside a for loop, there's delay only on the first time, and I wait the delay to kick in every time the for loop repeats:
For example, in this case:
for(int z=0; z<4; z++) {
final int finalZ = z;
handler.postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(play.this, "z:" + finalZ, Toast.LENGTH_SHORT).show();
}
}, 5000);
}
I will get:
Waiting 5 seconds.
z:0
z:1
z:2
z:3
What I want to get:
Waiting 5 seconds.
z:0
Waiting 5 seconds.
z:1
Waiting 5 seconds.
z:2
Waiting 5 seconds.
z:3
I was told not to use Thread.sleep() because it can cause various issues (I didn't quite understand them as I am new to android studio). I am using this in a certain activity (not my Main_Activity).
I am basically trying to change an image's color ever 0.5 seconds or so (more complications go into that, but that's the main idea). Will Thread.sleep() be better?
EDIT: Okay, so thanks to #tynn & #pskink I got this to work (see their answers). But now, another problem came up following this.
If, for example, I will run the exact same code after that, they will both run at the same time, and not one after another, how can i make the second "for" start only after the first "for" has ended?
Here another aspect
private int z=0;
Runnable r = new Runnable() {
#Override
public void run() {
Toast.makeText(play.this, "z:" + z, Toast.LENGTH_SHORT).show();
if(z<4){
z++;
handler.postDelayed(r, 5000);
}
}
}
handler.post(r);
The handler runs your code on another thread, independent of your for loop. The delay is relative to the point where you posted the runnable. Not relative to the previous post. Also it doesn't block the for loop at all. That's the purpose of handlers.
You can assume that any post in this scenario happens at the same time, so you could just do handler.postDelayed(runnable, (z + 1) * 5000). Or maybe define the execution time as absolute with handler.postAtTime(runnable, firstRun + z * 5000)
But since you're trying to do something more complicated than that, you should better consider using a Timer or similar. Maybe RxJava.
Your current code works like this:
hey handler, do it after 5s,
oh and do it after 5s too,
and do it after 5s too,
and this plz do after 5s.
Then we wait 5s and handler is doing those actions in one moment.
What you can do is change it to:
hey handler, do it after 5s,
oh and do it after 10s,
and this after 15s
...
Or you can do it like this:
hey handler do it after 5s, where 'it' action is 'hey handler do it after 5s'.
Then handler will wait 5s and call action 'hey handler do it after 5s'. So handler will have to wait again 5s and do some action after that time.
Here is code for my aproach:
final int finalZ = z;
delayedAction(handler, 0, z);
. . .
void delayedAction(final Handler handler, final int i, final int max) {
if(i<max) {
Toast.makeText(play.this, "z:" + finalZ, Toast.LENGTH_SHORT).show();
handler.postDelayed(new Runnable() {
#Override
public void run() {
delayedAction(handler, i+1, max);
}
});
}
}
I have been using 5 Handler tasks in my application with different post delays but all of them are working at a different speed irrespective of the delay time that I have specified, is there a better way to get exact time delay in between of multiple tasks,
Sample Code:
H2 = new Handler();
R2 = new Runnable()
{ #Override
public void run()
{ H2.postDelayed(R2, 100);
//Do something
}
};
H2.postDelayed(R2, 0);
Task would run in variable time intervals sometimes way more variable.
I'm trying to create a timer, that after the timer ends, will call a function...
For example, I have the function Foo. I want to create a timer, that after 1.5 seconds will call it..
Something like :
Timer(Foo(), 2000);
I have found this code :
private Handler handler = new Handler(); // Creating new handler
handler.postDelayed(runnable, 1500); // Creating a timer for 1.5 seconds
and this function :
private Runnable runnable = new Runnable()
{
#Override
public void run()
{
Foo();
handler.postDelayed(this, 1500);
}
};
My problem is, that some times the timer works perfect, usually for the first 2~3 times, and after that, Instead of being a 1.5sec timer, it become something like 0.3sec timer (and the more handler.postDelayed(runnable, 1500); is being called, the less time the timer will last (like, wont wait 1.5sec to call Foo, but much less)
Why is that ?
I know that in C++ if I write Console Applications, I can use Sleep.. Maybe I can just do something like this :
Sleep(1500);
Foo();
Thanks!
Edit: I have answered my own question.
You could use the Timer class in Android, and set a repeating timer, with a initial delay.
Timer timer = new Timer();
timer.schedule(TimerTask task, long delay, long period)
A TimerTask is very much like a Runnable.
See: http://developer.android.com/reference/java/util/Timer.html
I've used 2 timers :
handler.postDelayed(runnable, 1500); // Creating a timer for 1.5 seconds
this created a 1.5sec timer, while inside the timer loop :
private Runnable runnable = new Runnable()
{
#Override
public void run()
{
Foo();
handler.postDelayed(this, 1500);
}
};
I called handler.postDelayed(this,1500); again, which made 2 timers -> causing the time bug.
I want to run some code for 20 seconds precisely. It is similar to a loop but instead of having a variable I have time (in seconds).
I should have a time condition like this:
do
{ variable++ }
while (sec < 20)
How it is possible to do this in Android??
My application should run this 20 sec code after the user presses a button.
You can use the Handler class in Android on a runnable and then use the postDelayed() method. That way you will be able to update the UI during that 20 seconds on the progress of the thread. A good example of this is hear. Your code might look something like this ...
Handler handler = new Handler();
final Runnable r = new Runnable(){
public void run() {
//Do thing after 20 sec
}
};
handler.postDelayed(r, 20000);
I am trying to create a kind of a metronome for Android. It shall play some audible beeps at certain intervals to give people a rhythm. Hence, timing is quite critical. Essentially it shall do the following
(start)
Play beep type 1 (about 0.1s long)
Wait for x milliseconds (between 500 and 1000)
Play beep type 2 (about 0.1s long)
Wait for y milliseconds (between 500 and 1000)
Play beep type 3 (about 0.1s long)
Wait for y milliseconds (between 500 and 1000)
Go back to start
The UI activity will have some buttons to adjust the wait intervals.
From reading all the various blogs, tutorials and discussions it seems that I should be using a postdelayed() Runnable and set the delay to my desired wait time x or y. After that I should start playing the sound. This should allow me to not take the length of the wav sound file into account.
Am I roughly on the right track? I realise that the timing will not be perfect as there are other services running which might delay the execution of my timers. In order to improve that I'd be happy to use the phone in flight mode or turn of some other services as I don't need anything else when using this app.
Are there any full examples for such code out there? I am a beginner when it comes to Android. My eyperience is more with straight C embedded systems. Putting all the classes and their functions together is quite daunting.
Any help appreciated,
Michael
An idea that just came to my mind regarding the waiting between the beeps would be to launch a separate Java thread where you enter in an infinite loop and call Thread.sleep(interval) like
public MyLooper extends Runnable{
private boolean shouldRun = true;
private int interval = 1; //ms
#Override
public void run(){
while(shouldRun){
//play the beep
Thread.sleep(interval);
}
}
public void stop(){
this.shouldRun = false;
}
public void setInterval(int interval){
this.interval = interval;
}
}
When you launch your activity (depending on your needs) usually in the onResume event, you'd create the Java thread. Inside the button clicks you could then adjust the interval by calling the setInterval(...) method.
Could be a possible solution...
This is what you need:
Updating the UI from a Timer
EDIT: That tutorial is specifically targeting a UI-update scenario, but it gives a simple description of what you really want: a TimerTask
class UpdateTimeTask extends TimerTask {
public void run() {
long millis = System.currentTimeMillis() - startTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
timeLabel.setText(String.format("%d:%02d", minutes, seconds));
}
}
and
if(startTime == 0L) {
startTime = evt.getWhen();
timer = new Timer();
timer.schedule(new UpdateTimeTask(), 100, 200);
}