179

What is the proper way to set a timer in android in order to kick off a task (a function that I create which does not change the UI)? Use this the Java way: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Timer.html

Or there is a better way in android (android's handler)?

Nike15
  • 544
  • 3
  • 16
n179911
  • 19,547
  • 46
  • 120
  • 162

13 Answers13

167

yes java's timer can be used, but as the question asks for better way (for mobile). Which is explained Here.


For the sake of StackOverflow:

Since Timer creates a new thread it may be considered heavy,

if all you need is to get is a call back while the activity is running a Handler can be used in conjunction with a

Runnable:

private final int interval = 1000; // 1 Second
private Handler handler = new Handler();
private Runnable runnable = new Runnable(){
    public void run() {
        Toast.makeText(MyActivity.this, "C'Mom no hands!", Toast.LENGTH_SHORT).show();
    }
};
...
handler.postAtTime(runnable, System.currentTimeMillis()+interval);
handler.postDelayed(runnable, interval);

or a Message

private final int EVENT1 = 1; 
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {         
        case Event1:
            Toast.makeText(MyActivity.this, "Event 1", Toast.LENGTH_SHORT).show();
            break;
        
        default:
            Toast.makeText(MyActivity.this, "Unhandled", Toast.LENGTH_SHORT).show();
            break;
        }
    }
};

...

Message msg = handler.obtainMessage(EVENT1);
handler.sendMessageAtTime(msg, System.currentTimeMillis()+interval);
handler.sendMessageDelayed(msg, interval);

on a side note this approach can be used, if you want to run a piece of code in the UI thread from an another thread.

WARNING: Handler's timer (or whatever controls delays) gets paused whenever the CPU goes to deep-sleep, but will continue once CPU wakes up (from where it was paused).

if you need to get a call back even if your activity is not running then, you can use an AlarmManager.

Top-Master
  • 7,611
  • 5
  • 39
  • 71
Samuel
  • 9,883
  • 5
  • 45
  • 57
  • 1
    updated the link, talk about google having trouble maintaining links. – Samuel Jul 10 '12 at 01:17
  • 1
    This article from 2007 - not saying it's wrong, but I'm always suspicious of a mobile article if it's older than 3 years. Things change pretty quickly. – StackOverflowed Sep 22 '12 at 13:13
  • @StackOverflowed, You missed the point buddy! A thread is always thicker than a handler. Bottomline, It depends on the (task's)weight you want to lift and not the age of the (this)thread. ;) – Samuel Sep 24 '12 at 09:35
  • 1
    it would be more like StackOverflow if you could quote the main points of your link here in your answer. – n611x007 Sep 30 '13 at 07:56
  • 1
    @naxa, my two cents to make this response more like StackOverflow. – Samuel Oct 11 '13 at 08:14
  • +1 for "Since Timer creates a new thread it may be considered heavy". (Also, being a new thread means code on the timer tick isn't running on UI thread, so will get exception if attempt to update UI from the tick method.) – ToolmakerSteve Sep 12 '14 at 16:29
  • 2
    Just a note, import `android.os.Handler`, not `java.util.logging` – Ali Bdeir Sep 21 '16 at 17:25
147

Standard Java way to use timers via java.util.Timer and java.util.TimerTask works fine in Android, but you should be aware that this method creates a new thread.

You may consider using the very convenient Handler class (android.os.Handler) and send messages to the handler via sendMessageAtTime(android.os.Message, long) or sendMessageDelayed(android.os.Message, long). Once you receive a message, you can run desired tasks. Second option would be to create a Runnable object and schedule it via Handler's functions postAtTime(java.lang.Runnable, long) or postDelayed(java.lang.Runnable, long).

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
MannyNS
  • 4,703
  • 2
  • 22
  • 16
  • 10
    This is the wrong way to do things in android. In android, you want to user the Alarm Manager ( developer.android.com/reference/android/app/AlarmManager.html ) for running tasks far in the future. – Kurtis Nusbaum Oct 15 '11 at 04:16
  • 9
    @Kurtis Nusbaum The question doesn't say how far in the future the task is. – Christopher Perry Nov 05 '11 at 07:25
  • @Scienceprodigy And? Using the AlarmManager is still the more proper thing to do in this case. How far in the future the task is has nothing to do with it. – Kurtis Nusbaum Nov 05 '11 at 09:23
  • 70
    @KurtisNusbaum That's not necessarily true, it depends on the context. The docs on AlarmManager say, "Note: The Alarm Manager is intended for cases where you want to have your application code run at a specific time, even if your application is not currently running. For normal timing operations (ticks, timeouts, etc) it is easier and much more efficient to use Handler." – Christopher Perry Nov 05 '11 at 18:42
  • 6
    @Scienceprodigy Ah, I see. Fair enough. – Kurtis Nusbaum Nov 05 '11 at 19:07
  • 10
    using the Handler method of scheduling a task is reliable only if the application has acquired a wakeLock and hence you are sure that the phone shall not go into the sleep state. If phone does go to the sleep state then sendMessageDelayed as well as sendMessageAtTime will not work. Hence in that scenario AlarmManager would be reliable choice. – crazyaboutliv Jan 27 '12 at 21:59
  • @MannyNS where is postScheduled? Why no built in scheduling? – Pacerier Feb 14 '12 at 12:54
  • dandroid handler ticks – Zagorax Feb 06 '14 at 14:27
  • From the JavaDoc ; "By default, the task execution thread does not run as a daemon thread, so it is capable of keeping an application from terminating." - I wonder how Android handles this - in context of the application, fragment- or service lifecycle. For me, it seems safer to go with handler- since it is an Android contraption. – Glenn Bech Feb 16 '15 at 15:13
  • 1
    you should add **edit: i am wrong** for people who don't read the rest of the comments @KurtisNusbaum – sudocoder Oct 16 '19 at 18:22
  • In my opinion, the right way to do so is making use of [Chronometer](https://developer.android.com/reference/android/widget/Chronometer) UI component. – joninx Jul 15 '21 at 12:04
134

As I have seen it, java.util.Timer is the most used for implementing a timer.

For a repeating task:

new Timer().scheduleAtFixedRate(task, after, interval);

For a single run of a task:

new Timer().schedule(task, after);


task being the method to be executed
after the time to initial execution
(interval the time for repeating the execution)

n611x007
  • 8,952
  • 8
  • 59
  • 102
Thizzer
  • 16,153
  • 28
  • 98
  • 139
  • 11
    I'll just add that time is in milliseconds – Uri Dec 27 '12 at 03:40
  • 2
    android dev docs for [scheduleAtFixedRate](https://developer.android.com/reference/java/util/Timer.html#scheduleAtFixedRate%28java.util.TimerTask,%20long,%20long%29) – n611x007 Sep 30 '13 at 07:51
  • 1
    `task` may be an instance of your class that inherits from [java.util.TimerTask](https://developer.android.com/reference/java/util/TimerTask.html) and overrides `void run()`. – n611x007 Sep 30 '13 at 07:54
  • 2
    So many upvotes. You could to this. But @Samuel describes the better way. See his "Which is explained Here" link for why. Also, can't update UI from a Timer thread! And see these other Handler-based answers in other threads: (simple) http://stackoverflow.com/a/6702767/199364, or (shows various alternatives) http://stackoverflow.com/a/4598737/199364 – ToolmakerSteve Sep 12 '14 at 16:38
  • @quemeful because it is not about always about convenience, it is about code quality and efficiency. This way of doing it uses more resources then using the Handler – inDepth Jul 01 '19 at 07:53
51

I hope this one is helpful and may take less efforts to implement, Android CountDownTimer class

e.g.

 new CountDownTimer(30000, 1000) {
      public void onTick(long millisUntilFinished) {
          mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
      }

      public void onFinish() {
          mTextField.setText("done!");
      }  
}.start();
Arunjith R S
  • 792
  • 12
  • 23
Gaurav Adurkar
  • 834
  • 8
  • 9
19

Probably Timerconcept

new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

 public void onTick(long millisUntilFinished) {
  }
  public void onFinish() {
 }
}.start();

or

Method 2 ::

Program the timer

Add a new variable of int named time. Set it to 0. Add the following code to onCreate function in MainActivity.java.

//Declare the timer
Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {

    @Override
    public void run() {
        //Called each time when 1000 milliseconds (1 second) (the period parameter)
    }

},
//Set how long before to start calling the TimerTask (in milliseconds)
0,
//Set the amount of time between each execution (in milliseconds)
1000);

Go into the run method and add the following code.

//We must use this function in order to change the text view text
runOnUiThread(new Runnable() {

    @Override
    public void run() {
        TextView tv = (TextView) findViewById(R.id.main_timer_text);
        tv.setText(String.valueOf(time));
        time += 1;
    }

});
vkulkarni
  • 849
  • 1
  • 8
  • 12
11

Here we go.. We will need two classes. I am posting a code which changes mobile audio profile after each 5 seconds (5000 mili seconds) ...

Our 1st Class

public class ChangeProfileActivityMain extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        Timer timer = new Timer();
        TimerTask updateProfile = new CustomTimerTask(ChangeProfileActivityMain.this);
        timer.scheduleAtFixedRate(updateProfile, 0, 5000);
    }

}

Our 2nd Class

public class CustomTimerTask extends TimerTask {

    private AudioManager audioManager;
    private Context context;
    private Handler mHandler = new Handler();

    // Write Custom Constructor to pass Context
    public CustomTimerTask(Context con) {
        this.context = con;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

        // your code starts here.
        // I have used Thread and Handler as we can not show Toast without starting new thread when we are inside a thread.
        // As TimePicker has run() thread running., So We must show Toast through Handler.post in a new Thread. Thats how it works in Android..
        new Thread(new Runnable() {
            @Override
            public void run() {
                audioManager = (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
                            audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
                            Toast.makeText(context, "Ringer Mode set to Normal", Toast.LENGTH_SHORT).show();
                        } else {
                            audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
                            Toast.makeText(context, "Ringer Mode set to Silent", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        }).start();

    }

}
Rizwan Sohaib
  • 1,240
  • 17
  • 27
11

It is situational.

The Android documentation suggests that you should use AlarmManager to register an Intent that will fire at the specified time if your application may not be running.

Otherwise, you should use Handler.

Note: The Alarm Manager is intended for cases where you want to have your application code run at a specific time, even if your application is not currently running. For normal timing operations (ticks, timeouts, etc) it is easier and much more efficient to use Handler.

Timothy Lee Russell
  • 3,719
  • 1
  • 35
  • 43
5

I'm an Android newbie but here is the timer class I created based on the answers above. It works for my app but I welcome any suggestions.

Usage example:

...{
public Handler uiHandler = new Handler();

  private Runnable runMethod = new Runnable()
    {
        public void run()
        {
              // do something
        }
    };

    timer = new UITimer(handler, runMethod, timeoutSeconds*1000);       
        timer.start();
}...

public class UITimer
{
    private Handler handler;
    private Runnable runMethod;
    private int intervalMs;
    private boolean enabled = false;
    private boolean oneTime = false;

    public UITimer(Handler handler, Runnable runMethod, int intervalMs)
    {
        this.handler = handler;
        this.runMethod = runMethod;
        this.intervalMs = intervalMs;
    }

    public UITimer(Handler handler, Runnable runMethod, int intervalMs, boolean oneTime)
    {
        this(handler, runMethod, intervalMs);
        this.oneTime = oneTime;
    }

    public void start()
    {
        if (enabled)
            return;

        if (intervalMs < 1)
        {
            Log.e("timer start", "Invalid interval:" + intervalMs);
            return;
        }

        enabled = true;
        handler.postDelayed(timer_tick, intervalMs);        
    }

    public void stop()
    {
        if (!enabled)
            return;

        enabled = false;
        handler.removeCallbacks(runMethod);
        handler.removeCallbacks(timer_tick);
    }

    public boolean isEnabled()
    {
        return enabled;
    }

    private Runnable timer_tick = new Runnable()
    {
        public void run()
        {
            if (!enabled)
                return;

            handler.post(runMethod);

            if (oneTime)
            {
                enabled = false;
                return;
            }

            handler.postDelayed(timer_tick, intervalMs);
        }
    }; 
}
user868020
  • 51
  • 1
  • 1
3

I am using a handler and runnable to create a timer. I wrapper this in an abstract class. Just derive/implement it and you are good to go:

 public static abstract class SimpleTimer {
    abstract void onTimer();

    private Runnable runnableCode = null;
    private Handler handler = new Handler();

    void startDelayed(final int intervalMS, int delayMS) {
        runnableCode = new Runnable() {
            @Override
            public void run() {
                handler.postDelayed(runnableCode, intervalMS);
                onTimer();
            }
        };
        handler.postDelayed(runnableCode, delayMS);
    }

    void start(final int intervalMS) {
        startDelayed(intervalMS, 0);
    }

    void stop() {
        handler.removeCallbacks(runnableCode);
    }
}

Note that the handler.postDelayed is called before the code to be executed - this will make the timer more closed timed as "expected". However in cases were the timer runs to frequently and the task (onTimer()) is long - there might be overlaps. If you want to start counting intervalMS after the task is done, move the onTimer() call a line above.

elcuco
  • 8,948
  • 9
  • 47
  • 69
2

I believe the way to do this on the android is that you need a background service to be running. In that background application, create the timer. When the timer "ticks" (set the interval for how long you want to wait), launch your activity which you want to start.

http://developer.android.com/guide/topics/fundamentals.html (<-- this article explains the relationship between activities, services, intents and other core fundamentals of Android development)

Crowe T. Robot
  • 2,045
  • 17
  • 18
1

I used to use (Timer, TimerTask) as well as Handler to kick off (time-consuming) tasks periodically. Now I've switched the whole to RxJava. RxJava provides Observable.timer which is simpler, less error-prone, hassle-free to use.

public class BetterTimerFragment extends Fragment {
  public static final String TAG = "BetterTimer";
  private TextView timeView;
  private Subscription timerSubscription;

  @Override
  public View onCreateView(LayoutInflater inflater,
                           @Nullable ViewGroup container,
                           @Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_timer, container, false);
  }

  @Override
  public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    timeView = (TextView) view.findViewById(R.id.timeView);
  }

  @Override
  public void onResume() {
    super.onResume();

    // Right after the app is visible to users, delay 2 seconds
    // then kick off a (heavy) task every 10 seconds.
    timerSubscription = Observable.timer(2, 10, TimeUnit.SECONDS)
        .map(new Func1<Long, String>() {
          @Override
          public String call(Long unused) {
            // TODO: Probably do time-consuming work here.
            // This runs on a different thread than the main thread.
            return "Time: " + System.currentTimeMillis();
          }
        })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<String>() {
          @Override
          public void call(String timeText) {
            // The result will then be propagated back to the main thread.
            timeView.setText(timeText);
          }
        }, new Action1<Throwable>() {
          @Override
          public void call(Throwable throwable) {
            Log.e(TAG, throwable.getMessage(), throwable);
          }
        });
  }

  @Override
  public void onPause() {
    super.onPause();

    // Don't kick off tasks when the app gets invisible.
    timerSubscription.unsubscribe();
  }
}
Thuy Trinh
  • 2,914
  • 1
  • 22
  • 35
1

For timing operation you should use Handler.

If you need to run a background service the AlarmManager is the way to go.

Rahul Sharma
  • 2,867
  • 2
  • 27
  • 40
robsf
  • 1,713
  • 3
  • 14
  • 26
0

this example start the timer unitl destroyed in Kotlin

private lateinit var timerTask: TimerTask

 timerTask = object : TimerTask() {
        override fun run() {
            Log.d("KTZ", "$minutes:$seconds");
            timeRecordingLiveData.postValue("$minutes:$seconds")
            seconds += 1;
            if (seconds == 60) {
                Log.d("KTZ", "$minutes:$seconds");
                timeRecordingLiveData.postValue("$minutes:$seconds")
                seconds = 0;
                minutes += 1;
            }
        }

    }

Cancel the timertask in onDestroy()

timerTask.cancel()