How to change the delay of sleep/timer thread in android during runtime?

2.1k Views Asked by At

What I was trying to do was to decrease the timer delay every time the counter becomes a multiple of 5. But, as soon as the code entered the if block, it stopped incrementing the timer. I can't understand what is happening.

This is the code

thread=new Thread(){
    public void run(){
        try{
            if(count%5==0)
                timre--;
            else{
                //do nothing
            }
            //*******PROGRESS UPDATE********//
            for(t=0;t<=100;t++) {
                sleep((timre) / 100);
                progress.setProgress(t);
                t += 1;
            }
            //****PROGRESS UPDATE OVER****//
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            finish();
        }
    }//run ends
};//thread ends
thread.start();//timer starts
6

There are 6 best solutions below

0
On BEST ANSWER

Thread.sleep() is not guaranteed. This means that it may or may not sleep for the duration you desire for various reasons that are out of topic for this question.

If you search the net for "timer in android" you will probably land on these two: https://developer.android.com/reference/java/util/Timer.html and https://developer.android.com/reference/java/util/concurrent/ScheduledThreadPoolExecutor.html

You could check them out, however, I would not use those as they provide a lot of other functionalities as the name suggest ("ScheduledThreadPoolExecutor"). You don't need this as this is most likely to be used for big systems with lots of threads etc...

If I understand your code correctly you are trying to update a progress bar. For what you are trying to do I would suggest using a handler. One of the main uses of a handler is to schedule messages and runnables to be executed as some point in the future as specified in the docs here: http://developer.android.com/reference/android/os/Handler.html

I would do it like this:

 int counter = 0;
 int delayInMs = 5000; // 5 seconds
 Handler timer = new Handler(new Handler.Callback() {

 @Override
 public boolean handleMessage(Message msg) {
   counter++;
   // If the counter is mod 5 (or whatever) lower the delay
   if (counter % 5 == 0) {
     delayInMs/=100; // Or any other calculation.
   } 
   // If the counter reaches 100 the counting will not continue.
   if (counter <= 100) {
     // If the counter has not reached the condition to stop, the handler
     // will call the timer again with the modified (or not) delay.
     timer.sendEmptyMessageDelayed(0, delayInMs);

     // Change progress
     updateProgress(counter);
   }
   return true;
 }
});
// ...

// Somwhere in the code to start the counter
timer.sendEmptyMessageDelayed(0, delayInMs); // starts the timer with the initial 5 sec delay.

One more thing. In the line where you have this code:

progress.setProgress(t);

Be careful when calling UI elements from other threads it is the source of lot of headache. If your handler is in another thread you should wrap that call in a function and make sure it is called from the main thread (i.e. the UI thread). Many ways to achieve that (not always necessary thou). One of them is like this:

private void updateProgress(int counter) {
  WhateverActivity.this.runOnUiThread(new Runnable() {
    public void run() {
      progress.setProgress(counter);
    }
  });
}
1
On

Something like this should work :

thread=new Thread(){

++    private float timre;

++ public void setTimre(float timre) {
++    this.timre = timre;
++ }

public void run(){
    try{
        if(count%5==0)
            timre--;
        else{
            //do nothing
        }
        //*******PROGRESS UPDATE********//
        for(t=0;t<=100;t++) {
            sleep((timre) / 100);
            progress.setProgress(t);
            t += 1;
        }
        //****PROGRESS UPDATE OVER****//
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    finally {
        finish();
    }
}//run ends
};//thread ends

thread.start();

++ if(condition to change the sleeping time) {
++    try {
++        thread.interrupt();
++     catch(Exception e) {
++         e.doSomethingCoolWithit();
++    }

++    thread.setTimre(newTimre);
++    thread.start();
++ }
}

Although this will probably fail when trying to call thread.start(), for the second time, and you may need to create a new thread.

0
On

Threads (and sleep()) are tricky in android. Try using a CountDownTimer instead

CountDownTimer counter;
startTimer();
counter.start();

private void startTimer() {

    counter = new CountDownTimer(4000, 500){

        @Override
        public void onFinish() {

            **DO YOUR STUFF HERE**
        }
        @Override
        public void onTick(long millisUntilFinished) {
        }
    };
}

Call the function startTimer() wherever you want and start the counter at the same time.

0
On

as @Amaresh suggested, you should use the CountDownTimer to perform the task. Your implementation is flawed in two ways

  • There is no guarantee that a thread can always run and the timing within the thread may not be the same as the so-called real clock time. You can find more information about sleep and time from here.

  • I assume that you are going to update the UI when the progress changes. In this case, CountDownTimer saves you the trouble on how to make callback to main thread without extra handlers.

If you are interested, you can check the implementation of CountDownTimer from Android source code, it is straight forward and easy to understand.

In case that your code is running on a time consuming service, make sure that your timer actually works when the device becomes idle. In this case, you may need to implement wakeful alarm paired with WakefulBroadcastReceiver.

0
On

Could you please explain what timre and count are when this begins? I can presume they're 0 in which case,

if(count%5==0) //Immediately true. 0 mod 5 == 0.
  timre--; // 0-- = -1?
else{
  //do nothing
}

This would mean that your for loop is sleeping for -0.01 milliseconds?

for(t=0;t<=100;t++) {
  sleep((timre) / 100);
  progress.setProgress(t);
  t += 1;
}

This would explain why it's immediately running the setProgress method. But hey, I could be wrong. Let us know what those variables are initialized as or what they are currently set to when the code is hit!

Thanks,

0
On

I think that you should include the if block inside the for loop. As you have written the code, the if block only executes when the thread starts, so it is only executed once. Therefore, inside the loop the timre variable is never changed.

What I think that your code should look like is:

thread=new Thread(){
public void run(){
    try{

        //*******PROGRESS UPDATE********//
        for(t=0;t<=100;t++) {
            if(count%5==0)
               timre--;
            sleep((timre) / 100);
            progress.setProgress(t);
            t += 1;
        }
        //****PROGRESS UPDATE OVER****//
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    finally {
        finish();
    }
}//run ends
};//thread ends
thread.start();//timer starts