Handler.postDelayed causes lag - java

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

Related

How to loop through timer class for delayed time?

I want run timer for about 30000 ms and up to 8 or more times each so here is my loop but it runs all timers at once after 30000ms
public void repeatTimerTask() {
repeat = 8; // need to run 30 sec timer for 8 times but one after one
startTimer(30000); // firsat timer for 30 sec
Handler handler = new Handler();
for (int a = 1; a<=repeat; a++) {
final int finalA = a;
handler.postDelayed(new Runnable() {
#Override
public void run() {
startTimer(30000);
}
}, 30000); // delay until to finish first timer for 30 sec
}
}
To run a timer for n seconds you can use CountDownTimer
Declare two varibales globbaly. One for number of times you want to repeat. and one to keep the count of repetaion.
private int NUM_REPEAT = 4;
private int REPEAT_COUNT = 0;
Then call this method wherever you want. One thing to note if you want to run this loop 5 times you have to give number of repeation 4. Cause to satrt this function you have to call it so that will not come in count.
private void startTimer() {
new CountDownTimer(3000, 1000) {
int secondsLeft = 0;
public void onTick(long ms) {
if (Math.round((float) ms / 1000.0f) != secondsLeft) {
secondsLeft = Math.round((float) ms / 1000.0f);
// resend_timer is a textview
resend_timer.setText("remaining time is "+secondsLeft);
;
}
}
public void onFinish() {
Log.d(TAG, "timer finished "+REPEAT_COUNT);
if (REPEAT_COUNT <= NUM_REPEAT) {
startTimer();
REPEAT_COUNT++;
}
}
}.start();
}
Please try the below code, and call 'startTimer' method where you first want to start your timer :
private int startTimerCount = 1, repeat = 8;
private void startTimer(){
// if startTimerCount is less than 8 than the handle will be created
if(startTimerCount <= repeat){
// this will create a handler which invokes startTimer method after 30 seconds
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startTimer();
}
}, 30000);
// do what you want
Toast.makeText(this, "startTimer " + startTimerCount, Toast.LENGTH_SHORT).show();
}
startTimerCount++;
}

Why is JVM not instancing new TimerTask when using timer.schedule()?

I have a Timer in my Code, but the debugger jumps not into it. When it arrives at "timer.schedule()", jumps directly behind it and is not instancing the anonymous TimerTask. Can anyone help me why?
CODE:
timer = new Timer();
lastTimestamp = calendar.getTimeInMillis() / 1000;
// WHEN DEBUGGING IT JUMPS FROM THIS LINE
timer.schedule(new TimerTask() {
#Override
public void run() {
long now = calendar.getTimeInMillis() / 1000;
secondsElapsed += now - lastTimestamp;
lastTimestamp = now;
// Abbruch-Bedingung
if(secondsElapsed == totalSeconds)
{
// TODO:
Log.d("MainActivity", "Zeit abgelaufen");
}
else
{
int minutes = (int)secondsElapsed / 60;
int seconds = secondsElapsed - (minutes * 60);
final String newValue = String.format("%02d:%02d", minutes, seconds);
runOnUiThread(new Runnable() {
#Override
public void run() {
timerText.setText(newValue);
}
});
}
}
}, 0, 1000);
// TO THIS LINE
Those lines are executing in another thread. When that thread is scheduled to run they will run. If you place a breakpoint there you will see them execute
EDIT
try {
Timer timer = new Timer();
long lastTimestamp = System.currentTimeMillis() / 1000;
// WHEN DEBUGGING IT JUMPS FROM THIS LINE
timer.schedule(new TimerTask() {
#Override
public void run() {
System.out.println("WWWWWWW");
}
}, 0, 1000);
}
catch(Exception x){
}
}
This code executes with no problem. The thread is executed inside the timer

Update CountDownTimer with new Time - Android [duplicate]

The CountDownTimer default Constructor takes the values millisInFuture and countDownInterval.
If the user stops the timer, changes his settings to a different millisInFuture length, how would I go about changing the millisInFuture value without creating a new CountDownTimer object?
I've tried making a getMillisInFuture method in the override CountDownTimer class to no avail.
Would I have to override the onStart method, or what?
This class will not let you do much by itself.
You can create a class that contains a CountDownTimer timer field and a method update(time, tick) that hides the implementation. You would still need to call timer.cancel() and create a new CountDownTimer with the new values. Either that, or create your countdown timer from scratch using a Handler and postDelayed(...)
Take a look at an example of the second approach in my answer here
I needed it too, here's the code
public class DynamicCountdownTimer {
private CountDownTimer timer = null;
private double negativeBias = 0.00;
private double addingBias = 0.00;
private int minutes = 0;
private int ticks = 0;
private boolean supressFinish = false;
public DynamicCountdownTimer(int minutes, int ticks){
setTimer(minutes, ticks);
}
public void updateMinutes(int minutes){
if (timer != null){
this.supressFinish = true;
this.timer.cancel();
this.timer = null;
this.minutes = minutes;
this.addingBias = this.negativeBias + this.addingBias;
setTimer(this.minutes, this.ticks);
Start();
}
}
public void setTimer(int minutes, int ticks){
this.minutes = minutes;
this.ticks = ticks;
timer = new CountDownTimer((minutes * 60 * 1000), ticks) {
#Override
public void onTick(long l) {
negativeBias = (minutes * 60 * 1000) - l;
long calculatedTime = l - (long)addingBias;
if (calculatedTime <= 0){
onFinish();
}else{
callback.onTick(calculatedTime);
}
}
#Override
public void onFinish() {
if (!supressFinish){
callback.onFinish();
}
supressFinish = false;
}
};
}
public void Start(){
if (timer != null){
timer.start();
}
}
public void Cancel(){
if (timer != null){
timer.cancel();
}
}
public DynamicCountdownCallback callback = null;
public void setDynamicCountdownCallback(DynamicCountdownCallback c){
callback = c;
}
public interface DynamicCountdownCallback {
void onTick(long l);
void onFinish();
}
}
Here is how to use it:
DynamicCountdownTimer pCountDownTimer = null;
public void initializeTimer(int minutes){
pCountDownTimer = new DynamicCountdownTimer(minutes, 1000);
pCountDownTimer.setDynamicCountdownCallback(new DynamicCountdownTimer.DynamicCountdownCallback() {
#Override
public void onTick(long l) {
double progress = (double)( l) / (double)(minutes * 60 * 1000);
}
#Override
public void onFinish() {
// do something
}
});
pCountDownTimer.Start();
}
Then you can update it like this:
public void updateTimer(int minutes){
pCountDownTimer.updateMinutes(minutes);
}
After updating, the timer will simply carry on. It also carries over the already passed time.
This means, if the time was originally set to 30min and you update it to 45min after 10min, the remaining countdown time will be 35min.
It does it by recreating a new Timer when the update function is called. And at onTick, the already passed time (before the update) is calculated into the new progress.
You can change it to ms instead of min if you need it simply by replacing
(double)(minutes * 60 * 1000) -> ms
then
int minutes -> long ms

Change length of Countdowntimer

I want to change the length of my Countdowntimer when it finishs. So I got a Random Number which changes everytime.
standby_time = 15000
standby_counter = new CountDownTimer(standby_time,1000) {
#Override
public void onTick(long millisUntilFinished) {
++standby_zaehler;
}
#Override
public void onFinish() {
...
standby_zaehler = 0;
rndm_groesse = 60000;
random_zahl = r.nextInt(rndm_groesse);
standby_time = random_zahl;
standby_counter.start();
}
};
Where is my fault ? The time is not changing and takes the first value (15sec)
The countdown timers time isnt ment to be changed. You can recreate the timer to alter the time but because you are looking to start it again inside the onFinish() method it would be wise to use a different implementation better suited for this task. The java.util.Timer class does this well.
private Timer timer = new Timer();
private int standby_zaehler = 0;
private Random r = new Random();
public void scheduleNextTimer() {
// reset our countdown
standby_zaehler = 0;
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
// 1 second passed, remove it to the countdown
--standby_zaehler;
// we are finished counting down. get a new max seconds
if(standby_zaehler < 0) {
standby_zaehler = r.nextInt(60);
}
}
}, 1000, 1000);
}
As you can see we dont even need to create a new timer, just reset the number and begin again.

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