39

We have developed an Android Application which involves a service in the background. To implement this background service we have used IntentService. We want the application to poll the server every 60 seconds. So in the IntentService, the server is polled in a while loop. At the end of the while loop we have used Thread.sleep(60000) so that the next iteration starts only after 60 seconds.
But in the Logcat, I see that sometimes it takes the application more than 5 minutes to wake up (come out of that sleep and start the next iteration). It is never 1 minute as we want it to be.

What is the reason for this? Should background Services be implemented in a different way?

Problem2

Android kills this background process (intent service) after sometime. Can't exactly say when. But sometimes its hours and sometimes days before the background service gets killed. I would appreciate it if you would tell me the reason for this. Because Services are not meant to be killed. They are meant to run in background as long as we want it to.

Code :

@Override
 protected void onHandleIntent(Intent intent) {
  boolean temp=true;
  while(temp==true) {
    try {
      //connect to the server 
      //get the data and store it in the sqlite data base
    }
    catch(Exception e) {
      Log.v("Exception", "in while loop : "+e.toString());
    }
    //Sleep for 60 seconds
    Log.v("Sleeping", "Sleeping");
    Thread.sleep(60000);
    Log.v("Woke up", "Woke up");

    //After this a value is extracted from a table
    final Cursor cur=db.query("run_in_bg", null, null, null, null, null, null);
    cur.moveToLast();
    String present_value=cur.getString(0);
    if(present_value==null) {
       //Do nothing, let the while loop continue  
    }
    else if( present_value.equals("false") || present_value.equals("False") ) {
       //break out of the while loop
       db.close();
       temp=false;
       Log.v("run_in_bg", "false");
       Log.v("run_in_bg", "exiting while loop");
       break;
    }
  }

}

But whenever the service is killed, it happens when the the process is asleep. The last log reads - Sleeping : Sleeping. Why does the service gets killed?

DACrosby
  • 11,116
  • 3
  • 39
  • 51
Ashwin
  • 12,691
  • 31
  • 118
  • 190
  • 3
    You should use a `Timer` instead and schedule it at a fixed rate instead of using `Thread.sleep()` in a while loop. – Mxyk Aug 31 '12 at 16:31
  • 1
    Because they allow you to do exactly what you're trying to do - repeat a task on a certain interval. Not necessarily an answer to your question, but a suggestion (hence why I'm posting a comment, not an answer). There's also always the chance that using `Thread.sleep()` is the reason why it's taking 5 minutes - something maybe a `Timer` may not do? I figured it'd be worth a shot. – Mxyk Aug 31 '12 at 16:35
  • @Ashwin: It would be extremely nice if you could share the solution of this problem. I have been facing one such issue, but, none of the solutions suggested here seemed to worked. – User11012 May 21 '15 at 10:31
  • @User11012 : What is your problem exactly? – Ashwin May 21 '15 at 10:32
  • @Ashwin: I need to execute a function every minute. This function is making a POST call from the App to the Server and is transferring the location of the user per minute. The location coordinates are transferred for few hours, however, after few hours the transfer of location coordinates to the Server stops on its own. Code has been shared at this link: http://stackoverflow.com/questions/30334215/how-to-keep-the-cpu-of-android-phone-always-awake – User11012 May 21 '15 at 10:54
  • @Ashwin: Basically, my background service is being killed after some time, though for initial few hours it works perfectly well. It would be very kind of you, if you could please help. – User11012 May 22 '15 at 06:50

10 Answers10

82

The main problem is that we cannot say

Services are not meant to be killed. They are meant to run in background as long as we want it to.

Basically, that is not true. System still can terminate the service in low memory and possibly other situations. There are 2 ways to overcome this:

  1. If you are implementing the service, override onStartCommand() and return START_STICKY as the result. It will tell the system that even if it will want to kill your service due to low memory, it should re-create it as soon as memory will be back to normal.
  2. If you are not sure 1st approach will work - you'll have to use AlarmManager http://developer.android.com/reference/android/app/AlarmManager.html . That is a system service, which will execute actions when you'll tell, for example periodically. That will ensure that if your service will be terminated, or even the whole process will die(for example with force close) - it will be 100% restarted by AlarmManager.
starball
  • 20,030
  • 7
  • 43
  • 238
AlexN
  • 2,544
  • 16
  • 13
  • 6
    If that is not always true, then what about the background services of music players, yahoo mail, facebook etc. They never get killed. – Ashwin Sep 23 '12 at 04:59
  • 14
    Please check this method - http://developer.android.com/reference/android/app/Service.html#startForeground As described, heavy services that are running major user features like music playing - can startForeground() - and the system will try not to stop it. But you'll need to provide a notification to show user that background service is working. Also, there is still no guarantee that the system will not stop it, but it's much less probable. – AlexN Sep 24 '12 at 17:23
  • 7
    if I remember correctly if any process was forced to close AlarmManager won't start it again. – demaksee Nov 04 '13 at 15:53
  • Regarding problem with sleeping more then 60 sec - probably this is because phone became asleep and timer for Thread.sleep() also sleep (see SystemClock.uptimeMillis()). Prevent phone from sleeping can be solution in this case, but it is better to use AlarmManager or maybe push messages – demaksee Nov 04 '13 at 16:01
  • Are you saying that a background service, as music player, can be "paused" by Android? – richardaum Jun 28 '14 at 16:02
  • @demaksee, in my brief testing on Android 4.3, this is the case when the app crashes or is ended by the user, but not if it's killed as a result of low memory. – Sam Feb 18 '15 at 09:48
  • Does not work post 4.4.1 for me. http://stackoverflow.com/questions/29607856/android-service-gets-killed-on-swiping-out-the-application/29608040#29608040 – Sohaib Apr 13 '15 at 14:40
  • @AlexN Thank you very much! Your comment about startForeground() has really helped! – DmitryKanunnikoff Oct 22 '17 at 22:54
  • +1 for Alarm Manager. It's the best solution. However Google cloud messaging can also be of use for generic purposes. – Vijay Kumar Kanta Oct 05 '18 at 02:51
6

You could use ScheduledExecutorService designed specifically for such purpose.

Don't use Timers, as demonstrated in "Java Concurrency in Practice" they can be very inaccurate.

Alexander Kulyakhtin
  • 47,782
  • 38
  • 107
  • 158
  • +1 for "Don't use Timers". I was unaware of the inaccuracy issue and am looking at that now :). – Mxyk Aug 31 '12 at 17:21
  • so you're suggesting to use the `ScheduledExecutorService` to bind to the service every few minutes and if it fails, it'll be restarted ? – Someone Somewhere Dec 29 '15 at 22:16
  • It seems that it might be easily possible to use a re-launch method from `onTaskRemoved()` - be it Alarm Manager or ScheduledExecutorService – Someone Somewhere Dec 30 '15 at 00:02
5

IntentService is not intended to keep running in a while loop. The idea is to react to an Intent, do some processing and stop the service once done.

That does not mean that it's not working and I can't tell you why you see such long delays but the cleaner solution is to use some external source to poke the service periodically. Besides vanilla Java methods you can also have a look at the AlarmManager or a Handler as mentioned in the AlarmManager documentation.

The Handler way would work like this

public class TriggerActivity extends Activity implements Handler.Callback {
    // repeat task every 60 seconds
    private static final long REPEAT_TIME = 60 * 1000;
    // define a message id
    private static final int MSG_REPEAT = 42;

    private Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHandler = new Handler(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // start cycle immediately
        mHandler.sendEmptyMessage(MSG_REPEAT);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // stop cycle
        mHandler.removeMessages(MSG_REPEAT);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler = null;
    }

    @Override
    public boolean handleMessage(Message msg) {
        // enqueue next cycle
        mHandler.sendEmptyMessageDelayed(MSG_REPEAT, REPEAT_TIME);
        // then trigger something
        triggerAction();
        return true;
    }

    private void triggerAction() {
        // trigger the service
        Intent serviceIntent = new Intent(this, MyService.class);
        serviceIntent.setAction("com.test.intent.OPTIONAL_ACTION");
        startService(serviceIntent);
    }
}

A simple Activity (which could be extended to have that functionality in all your activities) that sends itself a Message all the time while it is running (here between onStart and onStop)

zapl
  • 63,179
  • 10
  • 123
  • 154
  • please see the problem 2 in my question. The service is getting killed. – Ashwin Sep 02 '12 at 07:11
  • Hey, I am trying your Alarm Manager solution, but the service is dying. I would really appreciate it if you could answer my question https://stackoverflow.com/questions/51460417/why-does-android-keep-killing-my-service – Ruchir Baronia Jul 25 '18 at 05:19
4

A better solution would be have an AlarmManager go off every 60 seconds. This AlarmManager then starts the service that polls the server, the service then starts a new AlarmManager, its a recursive solution that works quite well.

This solution will be more reliable as you dont have the threat of the Android OS killing your service, looming over you. As per API: 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.

In your UI/main activity etc, set this timer, to go off in 60 seconds:

long ct = System.currentTimeMillis(); //get current time
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);

   mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); //60 seconds is 60000 milliseconds

In yourservice.class you could have this, it checks the connection state, if its good it sets the timer to go off in another 60 seconds:

    public class yourservice extends IntentService {

                    public yourservice() { //needs this constructor
                        super("server checker");
                    }

                    @Override
                    protected void onHandleIntent(Intent intent) {
                        WifiManager wificheck = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);

                        if(check for a certain condition your app needs etc){
            //could check connection state here and stop if needed etc
                              stopSelf(); //stop service
                        }
                           else{ //poll the server again in 60 seconds
            long ct = System.currentTimeMillis();
            AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
            Intent i= new Intent(getApplicationContext(), yourservice.class);
            PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);

           mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); 
           stopSelf(); //stop service since its no longer needed and new alarm is set
                               }
                }
}
  • I have been having a few problems wih alarm mnager. Please see my question - http://stackoverflow.com/questions/12256434/using-intentexatras-with-alarm-manager – Ashwin Sep 06 '12 at 04:12
  • Hey, I am trying your Alarm Manager solution, but the service is dying. I would really appreciate it if you could answer my question https://stackoverflow.com/questions/51460417/why-does-android-keep-killing-my-service – Ruchir Baronia Jul 25 '18 at 05:19
2

Services get killed. Like app gets killed. It is Android philosophy that you can get killed at any time.

You should as other wrote not make the assumption that your backgroundservice runs forever.

But you can use a foreground service to drastically reduce the chance of getting killed/restarted. Note that this forces a notification which is always visible. For example music players, vpn applications and sportstracker use this API.

plaisthos
  • 6,255
  • 6
  • 35
  • 63
  • Hey, I am trying to use foreground service, but it is still dying. I would really appreciate it if you could answer my question https://stackoverflow.com/questions/51460417/why-does-android-keep-killing-my-service – Ruchir Baronia Jul 25 '18 at 05:20
1

For Problem 1, from vanilla Java, Thread.Sleep() is guaranted to wake the thread after the timer has expired, but not exactly after it has expired, it may be later depending mainly of the statuses of other threads, priority, etc.; so if you sleep your thread one second, then it will sleep at least a second, but it may be 10 depending of a lot of factors, i'm not very versed in Android development, but i'm pretty sure it's the same situation.

For Problem 2, services can be killed when memory get low or manually by the user, so as others have pointed probably using AlarmManager to restart your service after a certain time will help you to have it running all the time.

Rafael
  • 2,827
  • 1
  • 16
  • 17
  • If services can be killed, then what about the background services of music players, yahoo mail, facebook etc. They never get killed. – Ashwin Sep 23 '12 at 05:01
  • Music players are not idle while running, so they aren't killed automatically (you need to do it manually), mail and Facebook apps, spawn themselves if they're killed either manually or automatically. – Rafael Sep 24 '12 at 19:08
0

Sound like you should be using a Service instead of an IntentService but if you want to use an IntentService and have it run every 60 seconds you should use the AlarmManager instead of just telling the Thread to sleep.. IntentServices want to stop, let it and have AlarmManager wake it up when it should run again.

JustinMorris
  • 7,259
  • 3
  • 30
  • 36
  • please see the edit. will these problems be solved if we use Service instead of IntentService? – Ashwin Sep 01 '12 at 13:37
  • Hey, I am trying your Alarm Manager solution, but the service is dying. I would really appreciate it if you could answer my question https://stackoverflow.com/questions/51460417/why-does-android-keep-killing-my-service – Ruchir Baronia Jul 25 '18 at 05:20
0
It could be probably for two reasons..
  1. Either the while loop is creating an issue, it is making the handler to work until temp==true
  2. Adding to it is threads, that is creating long delays upto 6 seconds. In case, the system is working for a large database, creating long delays between each query will add on the system memory. When the memory usage for application become so huge that the system memory gets low, system has to terminate the process..

    Solution for the Problem..

  3. You could replace above with Alarm Manager to revoke system services after a particular interval of time using Alarm Manager.

  4. Also for getting intent back after the system recovers the application from termination, you should use START_REDELIVER_INTENT. It is to get your last working intent back after the application terminates. for its usage, study https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Sahil Mahajan Mj
  • 11,033
  • 8
  • 53
  • 100
0

Android is pretty good about killing off long running services. I have found CommonsWare's WakefulIntentService useful in my application: https://github.com/commonsguy/cwac-wakeful

It allows you to specify a time interval as you are trying to do by sleeping.

benkdev
  • 673
  • 2
  • 16
  • 32
0

You can try Jobscheduler implementation with JobService running in Background, which is recommended above Android O.

abhi.nalavade
  • 196
  • 3
  • 10