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:
Timer .
ScheduledThreadPoolExecutor.
Service.
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:
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.
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.
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.
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.
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.
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);
}});
}
}
}
In the onHandleIntent of my my IntentService class, I created handle containing a runnable which should be done after 20 seconds. Unfortunatly my service sleeps or is destroyed before this period.
I tried also with the CountDownTimer, but i had the same problem.
Do someone have any idea can I make the onHnadleIntent waiting? Thank you!
This is the code:
public class MyService extends IntentService {
//...
#Override
protected void onHandleIntent(Intent workIntent) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.i("20 seconds later","I am here");
}
}, 20000);
//...
}
//...
}
Don't use an IntentService. It is not designed for your scenario. Use a regular Service. Put your code in onStartCommand(). At the bottom of your run() method, call stopSelf() on the Service instance to shut it down.
You need to stop onHandleIntent from returning until the Runnable has completed. This can be achieved by using a CountdownLatch, which awaits at the end of your onHandleIntent method, and gets released at the end of the run method.
NOTE: this will cause the intent service to not process other Intents you sent it until the previous one has completed (after 20 seconds).
You may also want to obtain a partial wakelock at the start of onHandleIntent and release it at the end.
I recently just learned and developed a widget. I understand that the widget have an auto update at every 30 - 60 minutes minimum. Now I have been asked to reduced that auto update to 5 minutes.
So I have thought up for creating another Service Thread that is constantly running a countdown timer every 5 minutes and refreshes the app to check for any possible errors. These errors are actually ping tests results. If a server is down, I will execute a Toast Message to inform the user that this server is down.
So, how should I go about doing this? Or is there a better suggestion. Please enlighten me.
May this helps you:
Buddy use TimerTask to call after specific time interval
Timer timer = new Timer();
timer.schedule(new UpdateTimeTask(),1, TimeInterval);
and
class UpdateTimeTask extends TimerTask {
public void run()
{
// code here
}
}
Or You can Use AlaramManager also:
Set AlarmManager like this:
private static final int REPEAT_TIME_IN_SECONDS = 60; //repeat every 60 seconds
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(),
REPEAT_TIME_IN_SECONDS * 1000, pendingIntent);
Change AlarmManager.RTC to AlarmManager.RTC_WAKEUP if u want to wake up phone when it goes off. More about AlarmManager Click Here
Those two parameters also means that your alarm time will be System.currentTimeMilis() which is time in UTC.
I think better use runnable with postDelayed, like this:
private Handler handler = new Handler();
private Runnable runnable = new CustomRunnable();
private class CustomRunnable implements Runnable {
public void run() {
// your logic
handler.postDelayed(runnable, REFRESH_TIME);
}
}
REFRESH_TIME — your constant to refresh in millis. Just run once handler.postDelayed(runnable, REFRESH_TIME); where you want.
I am trying to give a LocationClient a two-minute period to connect before calling getLastLocation on it. Initially I implemented this with a Timer (and TimerTask), but because Timers do not work in sleepmode, I would like to translate it to an AlarmManager. However, I am a bit confused as to how to do this, considering an AlarmManager calls another class, whereas I want to remain in the same class and simply delay for a two-minute period.
This is how it looks with a Timer.
Timer theTimer = new Timer();
theTimer.schedule(new TimerTask() {
#Override
public void run() {
if(checkIfGooglePlay() && checkTime()) {
getPostLocation();
stopSelf();
mLocationClient.disconnect();
}
}
}, TWO_MINUTES);
Alarm Manager calls a broadcast receiver via a pending intent. Just make that BroadcastReceiver implementation a private subclass of the class that registers it. That way it has full access to class member variables and functions.
You may try using Java's Thread.sleep(). It's a static method so you don't need to instantiate a new Thread. It throws a InterruptedException, which, in simpler cases, shouldn't bother you.
From http://developer.android.com/reference/java/lang/Thread.html:
Causes the thread which sent this message to sleep for the given interval of time (given in milliseconds and nanoseconds).
For example my app, it starts and runs the service(which is meant to check the database for new messages) but as far as I know you can have a AlarmManager to keep opening the Service if so is not opened.
But can you have aAlarmManager inside the Service class to keep on checking for new message in the database? Instead of re-opening the Service class just keep checking the database?
I want to know if it is possible to have a AlarmManager inside the Service class to keep checking the database every X seconds, because the Service will open when the App is Launched
The real question:
Is it possible to have a AlarmManager inside the Service class that runs a Method() to check a database?
Use a Timer
Timer myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
/**
*
*Do something. CODE HERE
*/
}
}, 0, 30000); //30000 = 30 Seconds Interval.