124

I'm designing an app that has a recurring task of sending presence to a dedicated server as long as the app is in foreground.

In my searches across the web I saw a few different approaches and wanted to know what is the best way of doing this.

What is the best way to schedule a server call?

The options I saw were:

  1. Timer .

  2. ScheduledThreadPoolExecutor.

  3. Service.

  4. BroadcastReciever with AlarmManager.

What's your opinion?

EDIT:
The reason I need this is for a chat based app that sends all the user actions to a remote server.
i.e. user is typing a message, user is reading a message, user is online, user is offline etc.

This means that once every interval, I need to send the server what I'm doing, since I open a chat room with other people, they need to know what I'm doing.

Similar to the whatsapp message feedback mechanism: message looks delivered

EDIT #2:
Recurring tasks should now be scheduled almost always via the JobScheduler API (or FirebaseJobDispatcher for lower APIs) in order to prevent battery draining issues as can be read in the vitals section of the Android training

EDIT #3:
FirebaseJobDispatcher has been deprecated and replaced by Workmanager, which also incorporates features of JobScheduler.

Johannes Pertl
  • 460
  • 2
  • 12
thepoosh
  • 12,193
  • 14
  • 68
  • 129
  • 2
    BroaccastReceiver with AlarmManager is pretty straight-forward to use. It's the only one of the above alternatives I've tried. –  Jan 17 '13 at 10:03
  • 1
    There is little reason to use a Timer over a ScheduledThreadPoolExecutor, which is more flexible as it allows more than one background thread and has a better resolution (only useful for ms resolution) and allows exception handling. As for the AlarmManager, [this post](http://stackoverflow.com/questions/8939734/running-a-service-in-separate-thread-and-waking-it-every-10-minutes) gives some information about the difference. – assylias Jan 17 '13 at 10:12
  • For short running lifecycle, i.e. perform some task every 30 seconds in an activity currently in foreground, use ScheduledThreadPoolExecutor (or Timer) is more efficient. For long running lifecycle, i.e. perform some task every 1 hour in a background service, use AlarmManager gives more reliability. – yorkw Feb 15 '13 at 07:49
  • Why do you even need to schedule the sending? From your app description why do not you just send it in real-time? – iTech Feb 18 '13 at 18:07
  • because the user assumes you're online, using a timeout. meaning, if I haven't received a "presence" or "typing" message in the past X amount of time, I automatically assume you're not doing it – thepoosh Feb 19 '13 at 07:00
  • @thepoosh Hi any issue then please comment on my answer.If I can Then I will share my views ... – Md Maidul Islam Apr 10 '13 at 11:46
  • Since the question applies to foreground app, a runnable which starts itself again with postDelayed is also an option. I use this on small intervalls of a few seconds or minutes. It is easy to cancel since you have the handler in your activity/fragment. http://stackoverflow.com/questions/1921514/how-to-run-a-runnable-thread-in-android – Gunnar Bernstein Apr 01 '15 at 18:22
  • thanks @SuryaPrakashKushawah, please notice the date the question was asked The comment will never be seen by anyone since it's so low, just write an answer – thepoosh Apr 17 '19 at 07:13

5 Answers5

166

I am not sure but as per my knowledge I share my views. I always accept best answer if I am wrong .

Alarm Manager

The Alarm Manager holds a CPU wake lock as long as the alarm receiver's onReceive() method is executing. This guarantees that the phone will not sleep until you have finished handling the broadcast. Once onReceive() returns, the Alarm Manager releases this wake lock. This means that the phone will in some cases sleep as soon as your onReceive() method completes. If your alarm receiver called Context.startService(), it is possible that the phone will sleep before the requested service is launched. To prevent this, your BroadcastReceiver and Service will need to implement a separate wake lock policy to ensure that the phone continues running until the service becomes available.

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.

Timer

timer = new Timer();

    timer.scheduleAtFixedRate(new TimerTask() {

        synchronized public void run() {

            \\ here your todo;
            }

        }, TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(1));

Timer has some drawbacks that are solved by ScheduledThreadPoolExecutor. So it's not the best choice

ScheduledThreadPoolExecutor.

You can use java.util.Timer or ScheduledThreadPoolExecutor (preferred) to schedule an action to occur at regular intervals on a background thread.

Here is a sample using the latter:

ScheduledExecutorService scheduler =
    Executors.newSingleThreadScheduledExecutor();

scheduler.scheduleAtFixedRate
      (new Runnable() {
         public void run() {
            // call service
         }
      }, 0, 10, TimeUnit.MINUTES);

So I preferred ScheduledExecutorService

But Also think about that if the updates will occur while your application is running, you can use a Timer, as suggested in other answers, or the newer ScheduledThreadPoolExecutor. If your application will update even when it is not running, you should go with the AlarmManager.

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.

Take note that if you plan on updating when your application is turned off, once every ten minutes is quite frequent, and thus possibly a bit too power consuming.

JerabekJakub
  • 4,850
  • 4
  • 23
  • 32
Md Maidul Islam
  • 2,264
  • 3
  • 15
  • 22
  • I am trying this method out for a periodic task, but it does not seem to work http://stackoverflow.com/questions/27872016/scheduledthreadpoolexecutor-for-a-periodic-task-using-retrofit-just-firing-onc – dowjones123 Jan 10 '15 at 02:07
  • For simple things - like checking state every n-seconds -Timer will do. – IgorGanapolsky Jun 16 '15 at 20:43
  • 1
    @Maid786 What Should we use if we want to do some task (like sending Notifications) at interval of a Week or duration in Days? Will Alarm Manager take too much background calculation or processing for that? – Chintan Shah Oct 27 '15 at 13:35
30

Timer

As mentioned on the javadocs you are better off using a ScheduledThreadPoolExecutor.

ScheduledThreadPoolExecutor

Use this class when your use case requires multiple worker threads and the sleep interval is small. How small ? Well, I'd say about 15 minutes. The AlarmManager starts schedule intervals at this time and it seems to suggest that for smaller sleep intervals this class can be used. I do not have data to back the last statement. It is a hunch.

Service

Your service can be closed any time by the VM. Do not use services for recurring tasks. A recurring task can start a service, which is another matter entirely.

BroadcastReciever with AlarmManager

For longer sleep intervals (>15 minutes), this is the way to go. AlarmManager already has constants ( AlarmManager.INTERVAL_DAY ) suggesting that it can trigger tasks several days after it has initially been scheduled. It can also wake up the CPU to run your code.

You should use one of those solutions based on your timing and worker thread needs.

Deepak Bala
  • 10,632
  • 2
  • 34
  • 48
  • 1
    So what if I wanted to use the app, and every half hour I would want to make a back-up. But I don't want to make a back up while the app is not in use (that would be a total waste). Alarmmanager will continuously repeat the action until reboot (that's at least what I've heard). What would you recommend? ScheduledThreadPoolExecutor or Alarmmanager? – hasdrubal Oct 27 '13 at 15:29
14

I realize this is an old question and has been answered but this could help someone. In your activity

private ScheduledExecutorService scheduleTaskExecutor;

In onCreate

  scheduleTaskExecutor = Executors.newScheduledThreadPool(5);

    //Schedule a task to run every 5 seconds (or however long you want)
    scheduleTaskExecutor.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            // Do stuff here!

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    // Do stuff to update UI here!
                    Toast.makeText(MainActivity.this, "Its been 5 seconds", Toast.LENGTH_SHORT).show();
                }
            });

        }
    }, 0, 5, TimeUnit.SECONDS); // or .MINUTES, .HOURS etc.
Emzor
  • 1,320
  • 16
  • 27
2

Quoting the Scheduling Repeating Alarms - Understand the Trade-offs docs:

A common scenario for triggering an operation outside the lifetime of your app is syncing data with a server. This is a case where you might be tempted to use a repeating alarm. But if you own the server that is hosting your app's data, using Google Cloud Messaging (GCM) in conjunction with sync adapter is a better solution than AlarmManager. A sync adapter gives you all the same scheduling options as AlarmManager, but it offers you significantly more flexibility.

So, based on this, the best way to schedule a server call is using Google Cloud Messaging (GCM) in conjunction with sync adapter.

tato.rodrigo
  • 2,503
  • 19
  • 25
1

I have created on time task in which the task which user wants to repeat, add in the Custom TimeTask run() method. it is successfully reoccurring.

 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Timer;
 import java.util.TimerTask;

 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.TextView;
 import android.app.Activity;
 import android.content.Intent;

 public class MainActivity extends Activity {

     CheckBox optSingleShot;
     Button btnStart, btnCancel;
     TextView textCounter;

     Timer timer;
     MyTimerTask myTimerTask;

     int tobeShown = 0  ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    optSingleShot = (CheckBox)findViewById(R.id.singleshot);
    btnStart = (Button)findViewById(R.id.start);
    btnCancel = (Button)findViewById(R.id.cancel);
    textCounter = (TextView)findViewById(R.id.counter);
    tobeShown = 1;

    if(timer != null){
        timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }

    btnStart.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View arg0) {


            Intent i = new Intent(MainActivity.this, ActivityB.class);
            startActivity(i);

            /*if(timer != null){
                timer.cancel();
            }

            //re-schedule timer here
            //otherwise, IllegalStateException of
            //"TimerTask is scheduled already" 
            //will be thrown
            timer = new Timer();
            myTimerTask = new MyTimerTask();

            if(optSingleShot.isChecked()){
                //singleshot delay 1000 ms
                timer.schedule(myTimerTask, 1000);
            }else{
                //delay 1000ms, repeat in 5000ms
                timer.schedule(myTimerTask, 1000, 1000);
            }*/
        }});

    btnCancel.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View v) {
            if (timer!=null){
                timer.cancel();
                timer = null;
            }
        }
    });

}

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

    if(timer != null){
        timer.cancel();
    }

    //re-schedule timer here
    //otherwise, IllegalStateException of
    //"TimerTask is scheduled already" 
    //will be thrown
    timer = new Timer();
    myTimerTask = new MyTimerTask();

    if(optSingleShot.isChecked()){
        //singleshot delay 1000 ms
        timer.schedule(myTimerTask, 1000);
    }else{
        //delay 1000ms, repeat in 5000ms
        timer.schedule(myTimerTask, 1000, 1000);
    }
}


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

    if (timer!=null){
        timer.cancel();
        timer = null;
    }

}

@Override
protected void onStop() {
    super.onStop();

    if (timer!=null){
        timer.cancel();
        timer = null;
    }

}

class MyTimerTask extends TimerTask {

    @Override
    public void run() {

        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = 
                new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
        final String strDate = simpleDateFormat.format(calendar.getTime());

        runOnUiThread(new Runnable(){

            @Override
            public void run() {
                textCounter.setText(strDate);
            }});
    }
}

}

hitesh141
  • 947
  • 10
  • 25