Background tasks in Android devices with an API level less than 26 (Oreo)
public class NavigineReceiverExample extends BroadcastReceiver {
// here you need to receive some action from intent and depending on this action start service or set repeating alarm
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
try{
if (action.equals("START")) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0,
new Intent("WAKE"),
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmService = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null)
alarmService.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000, 60 * 1000, pendingIntent);
} else if (action.equals("WAKE")) {
context.startService(new Intent(context, NavigineService.class));
}
} catch (Throwable e) {
Logger.d(TAG, 1, Log.getStackTraceString(e));
}
}
}
Background tasks starting from Android Oreo
JobScheduler jobShedulerService = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobShedulerService.schedule(new JobInfo.Builder(ID, new ComponentName(context, NavigineJobService.class.getName()))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) // any network type
.setPeriodic(15 * 60 * 1000) // every 15 minutes
.setRequiresCharging(false) // if device should be charging
.build());
Common Alarm Managers with .set() or .setRepeating() will work well only when the device is not in Doze mode because Android will ignore them and reschedule for next time. Using .setAndAllowWhileIdle() will allow the alarm to be called, but the job scheduler will be ignored even in this case.
However, you can add . setOverrideDeadline(time in milliseconds) method to your job scheduler and it will run this scheduled job when the device exits Doze mode. Here is the code example for the onReceive method of our BroadcastReceiver extension:
public class NavigineBroadcastReceiverExample extends BroadcastReceiver {
// here you need to receive some action from intent and depending on this action start service or set repeating alarm
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
try{
if (action.equals("START")) {
Intent wakeIntent = new Intent("WAKE");
wakeIntent.setClass(context, YourReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0,
wakeIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmService = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null)
alarmService.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 15 * 60 * 1000, pendingIntent);
} else if (action.equals("WAKE")) {
JobScheduler jobShedulerService = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobShedulerService.schedule(new JobInfo.Builder(1, new ComponentName(context, YourJobClass.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setMinimumLatency(0L)
.setOverrideDeadline(15 * 60 * 1000)
.build());
Intent wakeIntent = new Intent("WAKE");
wakeIntent.setClass(context, YourReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0,
wakeIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmService = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null)
alarmService.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + Default.NAVIGINE_JOB_SERVICE_WAKEUP_TIME * 1000, pendingIntent);
}
} catch (Throwable e) {
Logger.d(TAG, 1, Log.getStackTraceString(e));
}
}
}
Foreground services will run without any cancels and without entering Doze mode, so your background tasks will run infinitely. But this solution has one disadvantage, your app will always have pinned notification in the devices’ status bar.
Here is the foreground service code example:
public class ForegroundService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, YourActivity.class), 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
// here is the code you wanna run in background
return START_NOT_STICKY;
}
private void createNotificationChannel() {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT);
getSystemService(NotificationManager.class).createNotificationChannel(serviceChannel);
}
}
Another way to run background tasks even in Doze mode to send notifications to your application from the server. These notifications will force your application to exit Doze mode and yourи job could be done. But in this case, you will need to spend more resources on running and maintaining your service. You can find more info about using Firebase Cloud Messaging to run your background services by this link.