I have strange problem with countdowntimer. Normally it works well, but sometimes (like 5% chance or so) after locking phone and unlocking it bugs like on video:
https://www.dropbox.com/s/1nbp63gmv4spzgf/bug.mp4
Here i set the timer:
private void startCurrentLessonTimer() {
if(lessonsBreak) {
lessonsBreakEnd.setText(String.format("%02d:%02d:%02d", (currentLessonTimeInMillis / 1000) / 3600, ((currentLessonTimeInMillis / 1000) % 3600) / 60,
(currentLessonTimeInMillis / 1000) % 60));
} else {
currentLessonEnd.setText(String.format("%02d:%02d:%02d", (currentLessonTimeInMillis / 1000) / 3600, ((currentLessonTimeInMillis / 1000) % 3600) / 60,
(currentLessonTimeInMillis / 1000) % 60));
}
currentLessonTimer = new CountDownTimer(currentLessonTimeInMillis, 1000) {
long hours = (currentLessonTimeInMillis / 1000) / 3600;
long minutes = ((currentLessonTimeInMillis / 1000) % 3600) / 60;
long seconds = (currentLessonTimeInMillis / 1000) % 60;
#Override
public void onTick(long millisUntilFinished) {
if(seconds == 0) {
seconds = 59;
if(minutes == 0 && hours > 0) {
minutes = 59;
hours--;
}
else {
minutes--;
}
} else {
seconds--;
}
if(lessonsBreak) {
lessonsBreakEnd.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds));
} else {
currentLessonEnd.setText(String.format("%02d:%02d:%02d", hours, minutes, seconds));
}
}
#Override
public void onFinish() {
getNextLesson();
getCurrentLesson();
}
}.start();
}
And this function (inside getData()) is called in onResume()
#Override
public void onResume() {
super.onResume();
getData();
}
onPause():
#Override
public void onPause() {
super.onPause();
clean();
}
clean():
private void clean() {
if(currentLessonTimer != null) {
currentLessonTimer.cancel();
currentLessonTimer = null;
}
}
I tried to use other countdowntimer, but this problem still occurs (but not that often).
Code: http://pastebin.com/nKsasJ1S
Any ideas?
It looks like you get multiple CountDownTimers refreshing your UI. This leads me to your clean() function like it's not doing it's job on every onPause(). So on next onResume() there will be multiple CountDownTimers.
I don't know how CountDownTimer works but the thing I can think of is that your CountDownTimer is start()ed but still not initialized (currentLessonTimer is still null) when onPause() is occurring not letting clean() to run so it'd be a race condition that could be avoided with a Service for example.
Related
I have following code in my service.
countDownTimer = new CountDownTimer(60000 * 3, 1000) {
#Override
public void onTick(long l) {
intentDSU.putExtra("remaind", TimeUnit.MILLISECONDS.toSeconds(l));
sendBroadcast(intentDSU);
}
#Override
public void onFinish() {
postHandler();
intentDSU.putExtra("remaind", 0);
sendBroadcast(intentDSU);
countDownTimer.start();
}
};
countDownTimer.start();
That's work fine, but when i try convert seconds to minutes and remaind of seconds on my activity
private void updateGUI(Intent intent){
if(intent.getExtras() != null){
long remaind = intent.getLongExtra("remaind", 0);
if(remaind == 0){
onRefresh();
}
long minutes = TimeUnit.SECONDS.toMinutes(remaind) - (TimeUnit.SECONDS.toHours(remaind)* 60);
long seconds = TimeUnit.SECONDS.toSeconds(remaind) - (TimeUnit.SECONDS.toMinutes(remaind) *60);
MenuItem menuItem = menu.findItem(R.id.action_timer);
if(menuItem != null){
if(minutes > 0){
menuItem.setTitle(String.format("%o min.", minutes));
}
else {
menuItem.setTitle(String.format("%o sec.", seconds));
}
}
}
}
I become notice unusual results, seconds will start 73 instead 60. I haven't idea, what's wrong?
you're code is correct, I quess you get problem in String.format("%o min.", minutes) and here String.format("%o sec.", seconds).
Try to use this.
MenuItem menuItem = menu.findItem(R.id.action_timer);
if(menuItem != null){
if(minutes > 0){
menuItem.setTitle(String.format("%s min.", Long.toString(minutes)));
}
else {
menuItem.setTitle(String.format("%s sec.", Long.toString(seconds)));
}
}
try this
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
long sec=TimeUnit.MILLISECONDS.toSeconds(passmillitimer);
I'm using CountDownTimer like so:
timer = new CountDownTimer(30 * 1000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
handleOnTick(millisUntilFinished);
}
#Override
public void onFinish() {
handleOnFinish();
playSound(ALERT_SECOND_FINISH);
}
};
timer.start();
}
private void handleOnTick(long millisUntilFinished) {
millisUntilFinished = millisUntilFinished / 1000;
tvTime.setText(
String.format(Locale.US,
"%d:%02d",
min, sec)
);
}
The problem is when handleOnTick is called, millisUntilFinished is ~29953 and millisUntilFinished = millisUntilFinished / 1000 equals 29 while I'm expecting 30!
So how can I handle this latency without using counter value or so? I used Bigdecimal and Rounding but it still passes values less than 30000.
Try this: millisUntilFinished = Math.round(millisUntilFinished / 1000.0f);
I have a Timer which it starts from 00:00 nice, but when I want to stop it I call cdTimer.cancel() but then I'm not able to restart the Timer with the old values and it starts again. I have this method :
private void StartTimer(){
final long EndTime = 3600;
cdtTimer = new CountDownTimer(EndTime*1000, 1000) {
public void onTick(long millisUntilFinished) {
long secondUntilFinished = (long) (millisUntilFinished/1000);
long secondsPassed = (EndTime - secondUntilFinished);
long minutesPassed = (long) (secondsPassed/60);
secondsPassed = secondsPassed%60;
tvCounterTimer.setText(String.format("%02d", minutesPassed) + ":" + String.format("%02d", secondsPassed));
}
public void onFinish() {
tvCounterTimer.setText("done!");
}
}.start();
}
I thought I could save the values minutesPassed and secondsPassed but then I should pass them by parameter, right?
To cancle a timer do this
timer.cancel();
timer.purge();
To restart a timer you will have to re instantiate the object and then schedule it. i will start again.
My intention is to create an timer which starts from 00:00:00 and hangs together with a recording function. This recording is done in a service so, the timer is also in the same service. If the app moves to the background, the recording and thus the timer keep on going and the app picks the time up at the onResume with a myService.getTime().
But I experience two strange things. The first is that my timer sometimes goed faster than a second, sometimes slower, sometimes jumps from for example 00:00:04 to 00:00:06 etc. There is no consitency in it. I use the code below, but there might be a better option to solve this? The second is that it is causing a lag on my button, although I am starting it in a service?
SERVICE
//////////TIMER FUNCTION START//////////
private void startTimerClick() {
if (stopped) {
startTime = System.currentTimeMillis() - elapsedTime;
} else {
startTime = System.currentTimeMillis();
}
mHandler.removeCallbacks(startTimer);
mHandler.postDelayed(startTimer, 0);
}
private void pauseTimerClick() {
mHandler.removeCallbacks(startTimer);
stopped = true;
}
private void stopTimerClick() {
mHandler.removeCallbacks(startTimer);
stopped = false;
}
private void startTimer() {
startTimer = new Runnable() {
public void run() {
elapsedTime = System.currentTimeMillis() - startTime;
updateTimer(elapsedTime);
mHandler.postDelayed(this, REFRESH_RATE);
}
};
}
private void updateTimer(float time) {
secs = (long) (time / 1000);
mins = (long) ((time / 1000) / 60);
hrs = (long) (((time / 1000) / 60) / 60);
/*
* Convert the seconds to String and format to ensure it has a leading
* zero when required
*/
secs = secs % 60;
seconds = String.valueOf(secs);
if (secs == 0) {
seconds = "00";
}
if (secs < 10 && secs > 0) {
seconds = "0" + seconds;
}
/* Convert the minutes to String and format the String */
mins = mins % 60;
minutes = String.valueOf(mins);
if (mins == 0) {
minutes = "00";
}
if (mins < 10 && mins > 0) {
minutes = "0" + minutes;
}
/* Convert the hours to String and format the String */
hours = String.valueOf(hrs);
if (hrs == 0) {
hours = "00";
}
if (hrs < 10 && hrs > 0) {
hours = "0" + hours;
}
}
//////////TIMER FUNCTION END////////////
public String getHours(){
return hours;
}
public String getMinutes(){
return minutes;
}
public String getSeconds(){
return seconds;
}
}
ACTIVITY(/FRAGMENT)
private void timerStart() {
handler = new Handler();
Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
handler.post(new Runnable() {
#Override
public void run() {
timer.setText(myService.getHours()+":"+myService.getMinutes()+":"+myService.getSeconds());
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
}
You are using Threads both in your Service and in your Activity/Fragment code.
Using threads for time sensitive tasks in Android is a problem because Android is able to significantly delay threads.
I have been using a ScheduledThreadPoolExecutor for a similar task and it worked great.
You are then using it like this:
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); // where 1 is the number of needed concurrent threads. 1 should last for your needs.
executor.scheduleWithFixedDelay(new TimerTask() {
// your recurringly executed code here
}, 0, 1, TimeUnit.SECONDS);
Basically I am doing a cardio feature and have three countdown timers in a row nested within each other, so when one timer finishes, the next one starts. One for preparation time, one for workout time and one for rest time, the user chooses the times of these.
I need it to loop however many times the user selects from a numberpicker, but no matter what I do it only goes through it once and doesn't loop so I know it all works it's just the looping part that doesn't work.
Am I missing something here? Is there a better way to do this?
//Main countdown timers loop
for(int i = 0; i <= times.getValue() + 1; i++) //times NumberPicker
{
prepCountTimer = new CountDownTimer(_finalPrep * 1000, 1000) {
public void onTick(long millisUntilFinished) {
tvRoundCount.setText("Round " + roundCount + " / " + times.getValue());
tvCountDown.setText((millisUntilFinished / 1000) + "s");
if(millisUntilFinished <= (6 * 1000))
{
tvCountDown.setTextColor(Color.RED);
}
}
public void onFinish() {
workoutCountTimer = new CountDownTimer(_finalWorkout * 1000, 1000) {
public void onTick(long millisUntilFinished) {
tvCountDown.setTextColor(Color.GREEN);
tvCountDown.setText((millisUntilFinished / 1000) + "s");
if(millisUntilFinished <= 6 * 1000)
{
tvCountDown.setTextColor(Color.RED);
}
}
public void onFinish() {
restCountTimer = new CountDownTimer(_finalRest * 1000, 1000) {
public void onTick(long millisUntilFinished) {
tvCountDown.setTextColor(Color.GREEN);
tvCountDown.setText((millisUntilFinished / 1000) + "s");
if(millisUntilFinished <= 6 * 1000)
{
tvCountDown.setTextColor(Color.RED);
}
}
public void onFinish() {
roundCount = roundCount + 1;
}
}.start();
}
}.start();
}
}.start();
}
the issue here is that you create prepCountTimer and assign on finish ect, then start it. then it reaches the end of for each and loops again making another preopCountTimer and starting it. you need to make your restCountTimer start the next preopCountTimer once it's done. unless I'm understanding something wrong here.
public void callingMethod() {
timerMethod(times.getValue());
// execution continues as your timer will run in a different thread
}
public void timerMethod(final int count) {
if (count == 0) {
// we have done the number of timers we want we can
// call whatever we wanted to once our timers were done
}
//you could use count to get the times for each timer here
startTimer(_finalPrep, new timerListner() {
#Override
public void timerFinish() {
//when timer 1 finishes we will start timer 2
startTimer(_finalWorkout, new timerListner() {
#Override
public void timerFinish() {
//when timer 2 finishes we will start timer 3
startTimer(_finalRest, new timerListner() {
#Override
public void timerFinish() {
//when timer 3 finishes we want to call the next timer in the list.
timerMethod(count - 1);
}
});
}
});
}
});
}
private interface timerListner {
void timerFinish();
}
public void startTimer(int timerTime, final timerListner onFinish) {
// you can pass in other parameters unqiue to each timer to this method aswell
CountDownTimer timer = new CountDownTimer(timerTime * 1000, 1000) {
public void onTick(long millisUntilFinished) {
tvRoundCount.setText("Round " + roundCount + " / " + times.getValue());
tvCountDown.setText((millisUntilFinished / 1000) + "s");
if (millisUntilFinished <= (6 * 1000)) {
tvCountDown.setTextColor(Color.RED);
}
}
#Override
public void onFinish() {
onFinish.timerFinish();
}
};
timer.start();
}