Android CountDownTimer Class Lagging Main Thread - java

I am trying to use android.os.CountDownTimer to literally show a countdown timer via a textview for fitness purposes. The issue I am having is the timer seems to be having trouble running on the main thread i.e. the countdown will jump 2-4 secs and is clearly being "lagged" - the timer is intended to be in an endless loop until a stop button is pressed.
I am new to Java and Android and cannot figure out how to get the countdown timer running and updating the UI without any conflicts or lagging.
I have attempted to put the CountDown in a Handler/Runnable and an Asynctask with no luck.
MainActivity
CountDownTimer timer;
void countdownTimer() {
long min = countdownMins * 60000;
long sec = countdownSecs * 1000;
long milliseconds = min+sec;
timer = null;
timer = new CountDownTimer(milliseconds, 1000) {
public void onTick(long millisUntilFinished) {
long mins = millisUntilFinished / 60000;
long secs = millisUntilFinished % 60000 / 1000;
String display = String.format("%02d:%02d", mins, secs);
tvTextView.setText(display);
}
public void onFinish() {
countdownTimer();
}
}.start();
}
Many thanks to anyone who can show me how to get this running off the main thread so my UI elements will be smooth.

I decided to take a different approach which has served my purposes very well. No lag or thread issues and can easily be restarted over and over. Hope this helps someone out.
int startCountdown = 5;
int currentCountdown;
Handler countdownHandler = new Handler();
Timer countdownTimer = new Timer();
public void startCountdownTimer() {
currentCountdown = startCountdown;
for (int i = 0; i <= startCountdown; i++) {
TimerTask task = new TimerTask() {
#Override
public void run() {
countdownHandler.post(doA);
}
};
countdownTimer.schedule(task, i * 1000);
}
}
final Runnable doA = new Runnable() {
#Override
public void run() {
if (currentCountdown != 0) {
tvTextView.setText("" + currentCountdown);
currentCountdown--;
} else {
currentCountdown = startCountdown;
startCountdownTimer();
}
}
};

Try to use Handler and runnable instead of CountDownTimer. Here is a good article about this approach
http://www.mopri.de/2010/timertask-bad-do-it-the-android-way-use-a-handler/
One more thing you could try is
void countdownTimer() {
final Runnable runnable = new Runnable() {
#Override
public void run() {
tvTextView.setText(display);
}
};
long min = countdownMins * 60000;
long sec = countdownSecs * 1000;
long milliseconds = min + sec;
timer = null;
timer = new CountDownTimer(milliseconds, 1000) {
public void onTick(long millisUntilFinished) {
long mins = millisUntilFinished / 60000;
long secs = millisUntilFinished % 60000 / 1000;
final String display = String.format("%02d:%02d", mins, secs);
textView.post(runnable);
}
public void onFinish() {
countdownTimer();
}
}.start();
}

If you countdown with changing your TextView fast and frequently. You have to set your TextView width and height in a fix number, or it will lag while calculating your view scale.

Related

Countdown Time in Android

I am developing an Android app and I have a requirement to:
1) Countdown time from 'x' minutes to '1' minute and then once it is at 1 minute, count in terms of 60 sec, 30 sec and then 0.
I am counting down in terms of 1 min(60000).
My code is:
public void countdowntimer(long timeinmillis, long countdowninterval){
Log.d("hi","Iamhere 0" + timeinmillis/60000);
new CountDownTimer(timeinmillis, countdowninterval) {
public void onTick(long timeinmillis) {
//Countdown the time in terms of minutes
Log.d("hi","Iamhere 2" + timeinmillis);
tv.setText(String.valueOf((timeinmillis)/60000) + "min");
rootView.invalidate();
if(timeinmillis <= 60000 && timeinmillis > 30000){
tv.setText(String.valueOf(60) + "sec");
rootView.invalidate();
}else if(timeinmillis < 30000){
tv.setText(String.valueOf(30) + "sec");
rootView.invalidate();
}
}
public void onFinish() {
}
}.start();
}
The logs are:
Iamhere0 4
Iamhere1 3
Why is my second log showing 1 minute lesser than first log and how do I implement 60sec, 30sec countdown once it is at 1 minute?
The expected output is:
4 min
3 min
2 min
1 min -> 60sec
30sec
0
It took me some time to answer it, this was my holydays ... but here is one other solution if this is still interresting for anyone.
I prefer to use System.currentTimeInMilli to manage the timer. When I start it, I calculate the time to end then on each tick, I calculate the remaining time to show.
This design prevent the delay you will find with any incremental/decremental variable timer. To keep this class reusable, I used the Observer pattern, on each tick (100ms on this example), the Observable instance will be send to each observer.
public class RunnableTimer extends Observable implements Runnable {
private final Handler handler;
// Number of milliseconds to run before the end
private Long delay;
// Date in millis until the end of the timer
private Long countDownEnd = null;
//The calculate remaining time
private long time;
public RunnableTimer(Observer o){
this.handler = new Handler();
addObserver(o);
}
/**
* Start the timer
* #param delay : the number of milliseconds to run
*/
public void start(Long delay){
this.delay = delay;
countDownEnd = System.currentTimeMillis() + delay;
handler.postDelayed(this, 0);
}
#Override
public void run() {
time = countDownEnd - System.currentTimeMillis();
if (time > 0)
handler.postDelayed(this, 100); //Recalculate every 100ms, to keep some precision... can be more or less.
else
time = 0; //to prevent to show negative values
sendUpdate();
}
public String toString(){
long t = time / 1000;
Log.d("time", "" + t);
if(t >= 60){
return String.format("%d min.", t/60);
} if t <= 0){
return "End";
} else {
return String.format("%d sec.", (t/30+1)*30);
}
}
/**
* Send the instance to each observer.
*/
private void sendUpdate(){
setChanged();
notifyObservers(toString());
}
}
From this, you have a timer that use the system time to calculate the remaining time and the toString method that create the desire template.
To use it, simply create an instance with the Observer you want.
public class Test implements Observer {
private RunnableTimer timer;
public Test(){
timer = new RunnableTimer(this);
timer.start(65000) //65sec
}
#Override
public void update(Observable observable, Object data) {
if(data == timer){ //If the update comes from the timer
Log.d("timer", timer.toString());
}
}
}
I had to clear a huge part of my class but this should works fine since this is simpler version.
EDIT : In your case, your activity / fragment should implements Observer and in the update, where I log the timer, you should put the toString result into you textfield.
try like this :
public class MyTimeCount extends CountDownTimer {
private Button btn_code;
public MyTimeCount(long millisInFuture, long countDownInterval,Button btn) {
super(millisInFuture, countDownInterval);
btn_code=btn;
}
#Override
public void onFinish() {
btn_code.setText("Reacquire");
btn_code.setEnabled(true);
}
#Override
public void onTick(long millisUntilFinished){
btn_code.setEnabled(false);
btn_code.setText(millisUntilFinished /1000+"s");
}
}
Use AtomicInteger to maintain the threads condition.
In First Condition its display time in minute
if (min.get() > 1) {
mCount.setText(Integer.toString(min.get())+" min");
handler.postDelayed(this, 1000);
min.getAndDecrement();
}
AND
In Last one minute its goes to second condition which display time in seconds
else {
if(min.get()== 1) {
mCount.setText("60 Sec");
handler.postDelayed(this, 500);
}
else if(min.get() == 0){
mCount.setText("30 Sec");
handler.postDelayed(this, 500);
}
else
mCount.setText("0 Sec");
min.getAndDecrement();
}
Use this ...........
final TextView mCount = (TextView) findViewById(R.id.count);
final Handler handler = new Handler();
final AtomicInteger min = new AtomicInteger(4);
final Runnable counter = new Runnable() {
#Override
public void run() {
if (min.get() > 1) {
mCount.setText(Integer.toString(min.get())+" min");
handler.postDelayed(this, 1000);
min.getAndDecrement();
} else {
if(min.get()== 1) {
mCount.setText("60 Sec");
handler.postDelayed(this, 500);
}
else if(min.get() == 0){
mCount.setText("30 Sec");
handler.postDelayed(this, 500);
}
else
mCount.setText("0 Sec");
min.getAndDecrement();
}
}
};
handler.postDelayed(counter, 0);
enjoy coding.....

Android: Forcing UI refresh immediately

I'm trying to build an android app which contains a countdown timer and should display three images for an amount of time in each interval - a masking image for 200 ms, a stimulus image for 20 ms and another masking image for again 200 ms.
The app works with two threads - the main UI thread and the time management thread.
The problem is, that the UI thread doesn't refresh itself, because it sleeps in between and hence doesn't display any images.
I've searched many hours already to find a way forcing the UI thread to refresh itself immediately, but until now I'm unsuccessful.
The methods invalidate() or postinvalidate() for example don't do anything useful.
It would be great if anyone has a hint or a solution for this problem.
Thanks for your help.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
Button button;
ImageView imageView;
TextView textViewCounter;
boolean buttonWasPressed = false;
double startTime;
double currentTime;
double timer;
final int INTERVALS = 2;
final double SECONDS_TO_NANOSECONDS_COEFFICIENT = 1000000000.0;
// length of the interval in seconds
final double INTERVAL_LENGTH = 10 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
int intervalCounter = 0;
// masking time in milliseconds (200)
final double MASKING_TIME = 0.2 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
// stimulus time in milliseconds (20)
final double STIMULUS_TIME = 0.02 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
boolean stimuliShouldBeDisplayed = false;
boolean stimuliIsDisplayed = false;
boolean imageViewShouldBeCleared = false;
Handler handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button);
if (button != null) {
button.setOnClickListener(this);
}
imageView = (ImageView) this.findViewById(R.id.imageView);
textViewCounter = (TextView) this.findViewById(R.id.textViewCounter);
// messages are sent to the thread where the Handler was created
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
// not sure if you must always clear the message queue
this.removeMessages(0);
double tempStartTime = System.nanoTime();
// milliseconds are okay
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
if (stimuliShouldBeDisplayed && !stimuliIsDisplayed) {
stimuliIsDisplayed = true;
// show mask
imageView.setImageResource(R.mipmap.mask);
try {
Thread.sleep((long) (MASKING_TIME / 1000000));
} catch (InterruptedException e) {
e.printStackTrace(
// update our timer (milliseconds are okay)
timer += System.nanoTime() - tempStartTime;
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
// show stimulus
imageView.setImageResource(R.mipmap.stimulus);
try {
Thread.sleep((long) (STIMULUS_TIME / 1000000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// update our timer (milliseconds are okay)
timer += System.nanoTime() - tempStartTime;
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
// show mask
imageView.setImageResource(R.mipmap.mask);
try {
Thread.sleep((long) (MASKING_TIME / 1000000));
} catch (InterruptedException e) {
e.printStackTrace();
}
// update our timer (milliseconds are okay)
timer += System.nanoTime() - tempStartTime;
textViewCounter.setText(String.valueOf(timer / 1000000) + " ms");
}
// clear the imageView
if (imageViewShouldBeCleared) {
imageView.setImageResource(0);
imageViewShouldBeCleared = false;
stimuliIsDisplayed = false;
stimuliShouldBeDisplayed = false;
}
}
};
}
#Override
public void onClick(View v) {
if (v == button && !buttonWasPressed) {
buttonWasPressed = true;
// let's start our timer
startTime = System.nanoTime();
Runnable runnableTimeManagement = new Runnable() {
#Override
public void run() {
while (currentTime - startTime <= INTERVAL_LENGTH && intervalCounter < INTERVALS) {
currentTime = System.nanoTime();
timer = currentTime - startTime;
// next interval
if (timer > INTERVAL_LENGTH) {
intervalCounter++;
startTime = currentTime;
imageViewShouldBeCleared = true;
}
// 1 seconds extra for the communication time between TimeManagement Thread and GUI Thread
if (timer + SECONDS_TO_NANOSECONDS_COEFFICIENT >= INTERVAL_LENGTH - 2 * MASKING_TIME - STIMULUS_TIME) {
stimuliShouldBeDisplayed = true;
}
// we must always create a new empty message
Message message = Message.obtain();
// we send message to the main UI thread
handler.sendMessage(message);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// time is over
buttonWasPressed = false;
intervalCounter = 0;
}
};
new Thread(runnableTimeManagement).start();
}
}
}
Does somebody know a way to precisely control the display time for the images in an alternative way ? The best solution is displaying the stimulus image for only one frame. But I don't know how to have access to the frame rate.
Ist there a possibility to force the UI thread to refresh itself immediately ?
Thread.sleep is usually not a good idea. And I'm not sure you need a secondary thread for what you describe. You could simply use Handler.sendMessageDelayed for UI updates to happen in the future:
// will run as soon as possible (almost immediately)
handler.sendMessage(handler.obtainMessage(0, R.mimap.mask, 0));
// will run in 200ms
handler.sendMessageDelayed(handler.obtainMessage(0, R.mimap.stimulus, 0), 200);
// will run in 220ms
handler.sendMessageDelayed(handler.obtainMessage(0, R.mimap.mask, 0), 220);
And then your handler only displays the image it received in the argument to the message:
#Override
public void handleMessage(Message msg) {
imageView.setResource(msg.arg1);
}
But keep in mind that you don't exactly control the refresh rate, so this won't guarantee that the image will be displayed for exactly 20ms

Count up timer on Android?

I've been looking for a way to create a timer that counts up in the format of mm:ss:SS and cannot for the life of me find a way of doing it. I had a timer running through a Handler and a Runnable but the timing was off and it took around 2.5 seconds to do a "second". I'll also need this timer be able to countdown too!
Can anyone give me any resources or code snippets to research on this as it is a big part of the app I'm coding.
Here's a bit of the code that I was using
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
#Override
public void run() {
/* do what you need to do */
testMethod();
/* and here comes the "trick" */
handler.postDelayed(this, 10);
}
};
public void testMethod()
{
// Log.d("Testing", "Test");
final TextView timeLabel = (TextView)findViewById(R.id.timeString);
count++;
seconds = (int)(count / 100);
final String str = ""+count;
runOnUiThread(new Runnable() {
public void run() {
timeLabel.setText("" + seconds);
// Log.d("Time", "" + count);
}
});
}
Ta!
Make small custom class by extending CountDownTimer class and then add integer or long type and then increment it, since each tick is 1 second (integer) in this case
public class TimeCounter extends CountDownTimer {
// this is my seconds up counter
int countUpTimer;
public TimeCounter(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
countUpTimer=0;
}
#Override
public void onTick(long l) {
//since each tick interval is one second
// just add 1 to this each time
myTextView.setText("Seconds:"+countUpTimer);
countUpTimer = countUpTimer+1;
}
#Override
public void onFinish() {
//reset counter to 0 if you want
countUpTimer=0;
}
}
TimeCounter timer = new TimeCounter(whenToStopInSeconds*1000, 1000);
This should get you started, in your case use long instead integer
countUpTimer = countUpTimer+1000 countUpTimer type and do time parsing as suits you
Rather than using the Handler, I'd recommend using the java.util.Timer and the java.util.TimerTask APIs. Use the Timer's void scheduleAtFixedRate() method which basically executes tasks after a fixed interval of time. The Handler's method most likely uses a fixed-delay execution.
Timer's Documentation
TimerTask's Documentation

Making a stopwatch using timer for javafx

I actually have a video streaming application and I want to show the time for how much the two people have chatted with eachother. I have used Timer and TimerTask of java.util class but it gives error as "Not on FX application thread" which means I cant setText to a java fx component using swing thread. This is what I have tried so far:-
int timerx=0 //global variable
private void timer(){
/*SHOWING TIME PASSED*/
int x=0;
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
System.out.println("working");
setTime();
}
};
timer.schedule(timerTask, 50, 50);
}
And updating the javafx Label as:-
private void setTime(){
timerx = timerx +1;
Platform.runLater(new Runnable(){
public void run(){
time.setText(String.valueOf((timerx)));
System.out.println(time.getText());
}
});
}
I think the main problem is javafx component not being able to update and be accessed from swing thread. I would be glad to get any kind of help.
Thank you
You can use something like this:
long timeStart = System.currentTimeMillis();
when the chat start and get how long two guys chatted with something like this:
long timePassed = System.currentTimeMillis() - timeStart;
This will get you how many millisecond have passed. If you want to get second divide it by 1000. Oh and if you want it on thread, just create a thread for this thingy..
ok thank you all for your answers. I solved my problem by running a thread and using algorithm that will show the time in 00:00:00 format which I wanted to make. Here is the code
private void startTime(){
if(timerStats==false)
{
timerStats = true;
timer = new Timer();
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
System.out.println("working" + x);
timersec ++;
Platform.runLater(new Runnable(){
public void run(){
if (timersec == 60)
{
timersec = 0;
timermin++;
}
if (timermin == 60)
{
timermin = 0;
timerhr++;
}
String seconds = Integer.toString(timersec);
String minutes = Integer.toString(timermin);
String hours = Integer.toString(timerhr);
if (timersec <= 9)
{
seconds = "0" + Integer.toString(timersec);
}
if (timermin <= 9)
{
minutes = "0" + Integer.toString(timermin);
}
if (timerhr <= 9)
{
hours = "0" + Integer.toString(timerhr);
}
time.setText(hours + ":" + minutes +":"+ seconds);
System.out.println(time.getText());
}
});
}
};
timer.schedule(timerTask, 50, 50); //lastone is time, milli second
}
}
Thank you

Making a program run for 5 minutes

So I wanted to try out something for a bit with the Timer and TimerTask classes.
I was able to get a line of code to execute after 30 seconds elapsed.
What I've been trying to do now is to get this line of code to execute for 5 minuets.
This is what I originally tried
public static void main(String[] args)
{
for ( int i = 0; i <= 10; i ++ )
{
Timer timer = new Timer();
timer.schedule( new TimerTask()
{
public void run()
{
System.out.println("30 Seconds Later");
}
}, 30000
);
}
}
I used the number 10 in the for loop to see if the timer.schedule would wait for another 30 seconds during the next iteration of the loop.
Any idea how I should go about this? I tried using the schedule method with a parameter passed in for period, but that only made it re-execute and it never stopped.
Java has provided a rich set of APIs in java.util.concurrent package to achieve such tasks. One of these APIs is ScheduledExecutorService. For example consider the code given below: This code will execute the Runnable task after every 30 seconds for upto 5 minutes:
import java.util.concurrent.*;
class Scheduler
{
private final ScheduledExecutorService service;
private final long period = 30;//Repeat interval
public Scheduler()
{
service = Executors.newScheduledThreadPool(1);
}
public void startScheduler(Runnable runnable)
{
final ScheduledFuture<?> handler = service.scheduleAtFixedRate(runnable,0,period,TimeUnit.SECONDS);//Will cause the task to execute after every 30 seconds
Runnable cancel = new Runnable()
{
#Override
public void run()
{
handler.cancel(true);
System.out.println("5 minutes over...Task is cancelled : "+handler.isCancelled());
}
};
service.schedule(cancel,5,TimeUnit.MINUTES);//Cancels the task after 5 minutes
}
public static void main(String st[])
{
Runnable task = new Runnable()//The task that you want to run
{
#Override
public void run()
{
System.out.println("I am a task");
}
};
Scheduler sc = new Scheduler();
sc.startScheduler(task);
}
}
The issue you're running into is that the scheduled Timer runs on a different thread - that is, the next iteration of your for loop starts running immediately after scheduling, not 30 seconds later. It looks like your code starts ten timers all at once, which means they should all print (roughly) 30 seconds later, all at once.
You were on the right track when you tried using the recurring version of schedule (with the third parameter). As you noted, this isn't quite what you want because it runs indefinitely. However, Timer does have a cancel method to prevent subsequent executions.
So, you should try something like:
final Timer timer = new Timer();
// Note that timer has been declared final, to allow use in anon. class below
timer.schedule( new TimerTask()
{
private int i = 10;
public void run()
{
System.out.println("30 Seconds Later");
if (--i < 1) timer.cancel(); // Count down ten times, then cancel
}
}, 30000, 30000 //Note the second argument for repetition
);
here's a workaround I'm ashamed of presenting:
package test;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class FiveMinutes {
private static int count = 0;
// main method just to add example
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
System.out.println("Count is: " + count);
if (count == 1) {
System.err.println("... quitting");
System.exit(0);
}
count++;
}
},
// starting now
new Date(),
// 5 minutes
300000l
);
}
}
Also please note that the application might not run exactly 5 minutes - see documentation for TimerTask.
Your solution is pretty close to working, you just have to multiply the delay by the counter (in your case, i):
public static void main(String[] args)
{
for (int i = 1; i <= 10; i++) // start i at 1 for initial delay
{
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run()
{
System.out.println("30 Seconds Later");
}
}, 30000 * i); // 5 second intervals
}
}
I don't know if this solution has problems with the garbage collector or not, but I throw it in here anyways. Maybe someone clears that out, and I learn something as well. Basically a timer sets a new timer if there is time left, and it should stop after 5 minutes.
Main.java:
public class Main {
public static void main(String[] args) {
MyTimer myTimer = new MyTimer(300000,30000);
myTimer.startTimer();
}
}
MyTimer.java:
import java.util.Timer;
import java.util.TimerTask;
public class MyTimer {
private int totalRunningTime;
private int currentTime = 0;
private int intervalTime;
private Timer timer = new Timer();
public MyTimer(int totalRunningTime, int intervalTime) {
this.totalRunningTime = totalRunningTime;
this.intervalTime = intervalTime;
}
public void startTimer() {
startTimer(intervalTime);
}
private void startTimer(int time) {
timer.schedule(new TimerTask() {
public void run() {
if (currentTime <= totalRunningTime - intervalTime) {
printTimeSinceLast(intervalTime / 1000);
currentTime += intervalTime;
startTimer(intervalTime);
} else if (currentTime < totalRunningTime) {
int newRestIntervalTime = totalRunningTime - currentTime;
printTimeSinceLast(newRestIntervalTime / 1000);
currentTime += newRestIntervalTime;
startTimer(newRestIntervalTime);
}
}
}, time);
}
private void printTimeSinceLast(int timeSinceLast) {
System.out.println(timeSinceLast + " seconds later.");
}
}

Categories

Resources