2

I want a service to be started every n minutes. This works, but only if the app is running (active on the screen or inactive, minimalised). When I close it (tap right bottom button and swipe to the side), the service stops being started.

Setting up the alarm (in MainActivity.onStart):

Intent startServiceIntent = new Intent(this, LogSyncService.class);
PendingIntent alarmIntent = PendingIntent.getService(this, 0, startServiceIntent, 0);

AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(
    AlarmManager.ELAPSED_REALTIME_WAKEUP,
    SystemClock.elapsedRealtime(),
    5 * 1000, // low for testing purposes
    alarmIntent);

AndroidManifest.xml:

<service
    android:name=".LogSyncService"
    android:description="@string/logSyncService_description"
    android:exported="false"/>

By looking at the alarm dump (adb shell dumpsys alarm), I can clearly see, that the alarm works even if the app is closed (num. of wakeups is increasing), but the service is not called, as it is when the app is open. When I reopen the app manually, all works fine again.

How do I solve this problem?

Martin Heralecký
  • 5,649
  • 3
  • 27
  • 65

1 Answers1

1

Alarms

Your should read how Doze and Standby are impacting alarms on devices with Android 6.0 and up. When phone goes to Doze state, all alarms and background jobs are stopped until maintenance period comes in. There is new alarms which can run in those states, setAndAllowWhileIdle() and setExactAndAllowWhileIdle() So basicly you should create new method where you will call different alarms methods based on platform version:

    public void setExactAlarm(int alarmType, long triggerTime, PendingIntent operation) {

    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

    if (alarmManager != null) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setExactAndAllowWhileIdle(alarmType, triggerTime, operation);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(alarmType, triggerTime, operation);
        } else {
            alarmManager.set(alarmType, triggerTime, operation);
        }
    }
}

And please note how alarms can be deferred in doze state:

Under normal system operation, it will not dispatch these alarms more than about every minute (at which point every such pending alarm is dispatched); when in low-power idle modes this duration may be significantly longer, such as 15 minutes.


Services

Things were changed in the latest SDK APIs. If you are running application on platform 26+ there is restriction on background services.

If your app targets API level 26 or higher, the system imposes restrictions on using or creating background services unless the app itself is in the foreground. If an app needs to create a foreground service, the app should call startForegroundService(). That method creates a background service, but the method signals to the system that the service will promote itself to the foreground. Once the service has been created, the service must call its startForeground() method within five seconds.

So, if you need some background service, now you must inform user that there is some service which executes in background via notification.

This is some sample code for your broadcast receiver when alarm fires up:

Intent service= new Intent(this, ExampleService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            context.startForegroundService(service);
        else
            context.startService(intent);

And last, inside of a service in onStartCommand() you must again check SDK version, and if version is 26+ you should call startForeground();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    Notification notification = new NotificationCompat.Builder(context, channalId);
    startForeground(NOTIFICATION_ID, notification);
}