51

I have a Service in my application which is designed to run every 10 minutes. It basically checks up on our servers to see if everything is running properly and notifies the user of any problems. I created this application for internal use at our company.

My co-worker used the application over the long weekend and noticed that no checks were performed when the device went to sleep. I was under the impression that the Service was supposed to keep running in the background until I explicitly call stopService() in my code.

So ultimately, my goal is to have the service running until the user hits the off button in the application or kills the process.

I heard about something called WakeLock which is meant to keep the screen from turning off, which is not what I want. I then heard of another thing called a partial WakeLock, which keeps the CPU running even when the device is asleep. The latter sounds closer to what I need.

How do I acquire this WakeLock and when should I release it and are there other ways around this?

Octavian Helm
  • 39,405
  • 19
  • 98
  • 102
PaulG
  • 6,920
  • 12
  • 54
  • 98
  • Indeed, you need a partial wakelock. Check the [Android PowerManager API](http://developer.android.com/reference/android/os/PowerManager.html) for an example how to get it. Release it when the user clicks your 'off' button. – THelper Jan 03 '12 at 14:08
  • Thought so, but the biggest turn off regarding WakeLock seems to be battery consumption. I'm guessing a partial WakeLock won't be as bad in that regard as a "full" WakeLock. But I could be wrong. – PaulG Jan 03 '12 at 14:19
  • 1
    Found more information about the "sleep" process at the following link if anyone's interested: http://stackoverflow.com/q/5120185/1030185 – PaulG Jan 03 '12 at 15:37
  • 1
    possible duplicate of [Run a service in the background forever..? Android](http://stackoverflow.com/questions/2322643/run-a-service-in-the-background-forever-android) – Marian Paździoch Mar 18 '15 at 07:51

3 Answers3

61

Note: This post has been updated to include the JobScheduler API of the Android Lollipop release. The following is still a viable way, but can be considered deprecated if you're targeting Android Lollipop and beyond. See the second half for the JobScheduler alternative.

One way to do recurrent tasks is this:

  • Create a class AlarmReceiver

    public class AlarmReceiver extends BroadcastReceiver 
    {
        @Override
        public void onReceive(Context context, Intent intent) 
        {
            Intent myService = new Intent(context, YourService.class);
            context.startService(myService);
        }
    }
    

    with YourService being your service ;-)

If you require a wake lock for your Task, it is advisable to extend from WakefulBroadcastReceiver. Don't forget to add the WAKE_LOCK permission in your Manifest in this case!

  • Create a Pending Intent

To start your recurrent polling, execute this code in your activity:

Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
//myAlarm.putExtra("project_id", project_id); //Put Extra if needed
PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Calendar updateTime = Calendar.getInstance();
//updateTime.setWhatever(0);    //set time to start first occurence of alarm 
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY, recurringAlarm); //you can modify the interval of course

This code sets up an alarm and a canceable pendingIntent. The alarmManager gets the job to repeat the recurringAlarm every day (third argument), but inexact so the CPU does wake up approximately after the interval but not exactly (It lets the OS choose the optimal time, which reduces battery drain). The first time the alarm (and thus the service) is started will be the time you choose to be updateTime.

  • last but not least: here is how to kill the recurring alarm

    Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
    //myAlarm.putExtra("project_id",project_id); //put the SAME extras
    PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager alarms = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarms.cancel(recurringAlarm);
    

This code creates a copy of your (probably) existing alarm and tells the alarmManager to cancel all alarms of that kind.

  • of course there is also something to do in the Manifest:

include these two lines

  < receiver android:name=".AlarmReceiver"></receiver>
  < service android:name=".YourService"></service>

inside the < application>-tag. Without it, the system does not accept the start of recurrent alarm of a service.


Starting with the Android Lollipop release, there's a new way of solving this task elegantly. This also makes it easier to only perform an action if certain criteria such as network state are met.

// wrap your stuff in a componentName
ComponentName mServiceComponent = new ComponentName(context, MyJobService.class);
// set up conditions for the job
JobInfo task = JobInfo.Builder(mJobId, mServiceComponent)
   .setPeriodic(mIntervalMillis)
   .setRequiresCharging(true) // default is "false"
   .setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED) // Parameter may be "ANY", "NONE" (=default) or "UNMETERED"
   .build();
// inform the system of the job
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(task);

You may also provide a deadline with setOverrideDeadline(maxExecutionDelayMillis).

To get rid of such a task, just call jobScheduler.cancel(mJobId); or jobScheduler.cancelAll();.

stefan
  • 10,215
  • 4
  • 49
  • 90
  • 1
    I'd upvote this answer if you'd add some explanation what the last two code blocks do. – Octavian Helm Jan 03 '12 at 14:40
  • added a bit. is it helpful now? – stefan Jan 03 '12 at 14:55
  • I'm reading up on this now and will give it a try. I particularly like the following line from the official Android documentation: "The Alarm Manager holds a CPU wake lock as long as the alarm receiver's onReceive() method is executing". Seems like it's just what I need. – PaulG Jan 03 '12 at 15:11
  • After playing around with AlarmManager for a few hours it finally seems to work. At least better than before. The timing is not always on target but I'm still working out the bugs. Thanks a lot. – PaulG Jan 03 '12 at 21:12
  • You're welcome. "not always on target" is by design: there is an alternative to ".setInexactRepeating(...)" but it's more battery-friendly if you let the cpu sleep a bit longer and let the alarm coincide with another. – stefan Jan 03 '12 at 22:36
  • 2
    Good explanation, but don't you still need a partial wakelock to prevent the started service from "falling asleep"? If you take a look at [this implementation](https://github.com/commonsguy/cwac-wakeful) you see that it also uses a partial wakelock plus alarm approach. – THelper Jan 04 '12 at 06:38
  • 1
    I don't think an additional wakelock is needed in _this_ case. It's just some data pull happening here so the service, which is intended to be of short life, should do its job pretty fast. – stefan Jan 04 '12 at 08:21
  • I'd like to also mention here that the AlarmReceiver should really extend [WakefulBroadcastReceiver](https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html) – Simon Jan 07 '15 at 14:54
  • @Simon thanks for your suggestion. I've added a note on that to my answer. – stefan Jan 07 '15 at 16:32
  • if I want a service to run like every 5-10 seconds. Is this way of doing the task is preferable. – Shuddh Oct 27 '16 at 14:31
  • @Shuddh It's certainly possible to do it this way, but as I'm not an active Android developer, I can't say if it's preferable. Please be aware of battery life when doing something repeatedly. Maybe there is a better way by bundling multiple actions together. – stefan Oct 27 '16 at 18:15
  • @stefan Right now Battery is not an issue for me as its the a tracking application. But instead of using Receiver, if I just keep the CPU active all the time will it be the right way to do? – Shuddh Oct 27 '16 at 21:09
  • I have a schedule which is running every 16 min which checks for if device time is changed. but the scheduler is not running while the device is in sleep. – Sagar Nayak Sep 15 '17 at 04:58
5

I would have recommended, if building this application from the beginning to use a server-side component (yes, would also need monitoring!) and send push notifications, polling is never a reliable solution.

Lee Hambley
  • 6,270
  • 5
  • 49
  • 81
  • 1
    Good idea, but some people don't have this option. In my case it could work as the servers are at our office and I have access to them, what if you need data from an outside source to be updated every 10 minutes? I'd still like to know how to keep a Service running. Thanks for the response. – PaulG Jan 03 '12 at 14:08
  • Sure, just that I've tried to push this water up hill before, and in the end decided to stop working against the OS (admittedly iOS) and implementing it “correctly” (although it does mandate more infrastructural overhead) – Lee Hambley Jan 03 '12 at 14:19
  • I've posted a pretty clean nifty solution here. Hope it helps. http://stackoverflow.com/questions/9029040/how-to-run-an-android-app-in-background/43555050#43555050 – user2288580 Apr 22 '17 at 04:02
3

From Android Documentation in doze mode following happens: (https://developer.android.com/training/monitoring-device-state/doze-standby):

The system ignores wake locks.

The system does not allow JobScheduler to run.

Android ignores AlarmManager as well unless they are in setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

Network access is suspended.

So the only way is to use FCM on high priority or AlarmManager with setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

Saba
  • 1,208
  • 14
  • 19
  • Saba, do you know if android services are also suspended when in doze mode. i assume so. all threads should be suspended but i wanted to get your opinion. Lets say we had a android service that was doing heavy work but not network related. in doze mode would the service suspend ? – j2emanue Jun 20 '19 at 10:46
  • 1
    Yes they will be suspended. – Saba Jun 21 '19 at 11:21
  • it seems foreground service is kept alive during doze mode. network operations cease but it can continue its work if declared as foreground. this exception was interesting. https://stackoverflow.com/questions/56866806/doze-mode-do-foreground-services-continue-to-run – j2emanue Jul 12 '19 at 04:52
  • @j2emanue note that, that depends on the Android version as well. – Saba Jul 14 '19 at 12:51