Background Services running forever in Android (Part 1)

Lakshya Punhani
4 min readJun 2, 2020

--

(Tested and verified in Android version 7,8,9,10)

With all the background execution limits starting from Android 8.0, it seems impossible to make an app running forever but still there are many ways. So without wasting any more time let’s explore.

Other parts of the series

Part 2

Part 3

WorkManager

WorkManager is a library used to enqueue deferrable work that is guaranteed to execute sometime after its Constraints are met. WorkManager allows observation of work status and the ability to create complex chains of work.

There are two types of work supported by WorkManager: OneTimeWorkRequest and PeriodicWorkRequest. You can enqueue requests using WorkManager as follows:

WorkManager workManager = WorkManager.getInstance(Context);
workManager.enqueue(new OneTimeWorkRequest.Builder(UploadLocationWorker.class).build());

Here UploadLocationWorker is a ListenableWorker class. All background work is given a maximum of ten minutes to finish its execution. After this time has expired, the worker will be signalled to stop. For more details please refer https://developer.android.com/reference/kotlin/androidx/work/WorkManager

So these are the basics of what WorkManager is but we are here to talk about running app in background forever so PeriodicWorkRequest is what we are looking to implement.

PeriodicWorkRequest workRequest = new PeriodicWorkRequest.
Builder(UploadLocation.class,15, TimeUnit.MINUTES)
.build();
WorkManager.getInstance(MainActivity.this).enqueue(workRequest);

This way you are initialiasing PeriodicWorkRequest which will run every 15 minutes

public class UploadLocation extends Worker {public UploadLocation(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public ListenableWorker.Result doWork() {
//Do all you work here
//Keep in mind the work you do here should gets complete in max 10 minutes
return Result.success();}
}

Now you will have questions like

  • Does this work when app gets killed?
  • What happens when phone enters Doze Mode?
  • If app goes to Standby State ?

Congratulations WorkManager works perfectly in every state.

Here are some test results done on various devices. I logged in time in worker thread

Oppo F11 Pro

Samsung S10

Mi A3

Vivo Z1 Pro

AlarmManager

Alarms (based on the AlarmManager class) give you a way to perform time-based operations outside the lifetime of your application. For more details please refer https://developer.android.com/training/scheduling/alarms and https://developer.android.com/reference/android/app/AlarmManager.

Now we know here what Alarm manager is and how this works but the complications are still there. Now the question arises how to make it work forever? Answers can be setRepeating() or setInexactRepeating() but NO they don’t work anymore.

Alarms do not fire when the device is idle in Doze mode. Any scheduled alarms will be deferred until the device exits Doze. You can use setAndAllowWhileIdle() or setExactAndAllowWhileIdle() to guarantee that the alarms will execute.

//Schedule Alarm Receiver in Main Activity
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), 234, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()
+ TimeUnit.SECONDS.toMillis(295),pendingIntent);
}
else
{
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()
+ TimeUnit.SECONDS.toMillis(295),pendingIntent);
}

This will schedule alarm now for receive after 295 seconds (Approx 5 minutes) and now in AlarmReceiver onReceive() method schedule same alarm again every time which will make it repeating alarm (Code below)

public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) { startAlarmReceiver(context);
}
public void startAlarmReceiver(Context context)
{
Intent intent = new Intent(context, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context.getApplicationContext(), 234, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime()+TimeUnit.SECONDS.toMillis(295),pendingIntent);}
else
{
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()+ TimeUnit.SECONDS.toMillis(295),pendingIntent);
}
}

Now, there are still some ways left to cover. Also if you will notice logs of vivo device you will see a lot of anomalies which we will cover in next blog why is this happening? Also we will study about the best practices for using workmanager

--

--