Keep a Service running even when phone is asleep?
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();
.
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.
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().