Android-job: Support foreground services

Created on 16 Jan 2018  路  15Comments  路  Source: evernote/android-job

I tested android-job v1.2.2 on Android 8.0 on the Oneplus 5.

The job is built as follows:
new JobRequest.Builder(jobTag)
.setBackoffCriteria(30000, JobRequest.BackoffPolicy.LINEAR) //30 seconds
.setExecutionWindow(1 * 1000, 3600 * 1000) //execute immediately if possible and at most 1 hour later
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
.addExtras(bundle)
.build()
.schedule();

If data saver mode is enabled, and the device is on a metered network, when the job is scheduled by the app when it's in the background (or if the app immediately moves into the background after the above code is executed), the job doesn't get executed until the app is brought to the foreground (presumably the network check fails).

However, if I create a foreground service myself right after scheduling the job, the library is successfully able to perform the network check, sees that internet connectivity is available and starts executing the job. It is also able to access network data in the background thanks to the foreground service. Without the foreground service, the job is not able to access data.

Could you please change the library to automatically create a foreground service so that the network connectivity check won't fail, and the job will get executed as expected? The foreground service must also be active while jobs are executing so that they can access the network, and can be stopped once all of the jobs have successfully completed.

It can be an option in the Job.Builder, so that only those who really need this feature can enable it.

enhancement

Most helpful comment

Huawei has a similar implementation, https://stackoverflow.com/a/51140127/1876355 There should be a similar intent for OnePlus as well. There are more of these solutions whether to check if your app is white listed and to ask the user to add your app.

All 15 comments

The library doesn't work well with foreground services and I'm not sure if this is even the right approach. The JobScheduler is intended for background work not foreground services. If you want to start a foreground service, then these are completely different requirements and you cannot use the JobScheduler.

One workaround for you could be not to enforce the requirements and start a foreground service from your job.

I'm not sure if I'll add this option to the library.

Well, we want to use it for a messaging app, for message sending jobs - we need to use a foreground service for the following reasons:

  • Heavily reduce possibility of app getting killed due to memory pressure while message sending jobs are executing
  • If the user has enabled data saver mode, background jobs cannot make network calls over cellular data - jobs don't get run even if network connectivity is present, since all network connectivity checks fail
  • Prevents app from going into app standby before/while executing message sending jobs
  • To work with Android 8.0 background restrictions

The main reason we're using jobs for this purpose is because an internet connection isn't always available at the time a message is sent. Our app persists the pending message request, and sends it out as soon as it can. Due to problems such as getting killed due to memory pressure/crashes, system going to sleep, app standby, data saver etc. the app doesn't always get woken up when internet connectivity is regained.

But with jobs, our message sending code will get executed even if the app was killed, or the device was rebooted.

Some problems we've found due to lack of foreground service creation by the library:

If there's no internet connectivity when the job is scheduled, the job doesn't get executed later when internet connectivity is regained, due to following problems:

  • Data saver is enabled. The library doesn't know it has an active internet connection that it can use so the job doesn't get executed.
  • App was moved to app standby mode. According to the docs, apps with foreground services won't be moved to app standby mode.
  • Light doze mode. App's jobs don't get executed when system regains internet connectivity.

If the library creates a foreground service when jobs are scheduled, app standby and data saver won't prevent jobs from executing.

I found that this library works better than GCMNetworkManager and Firebase job dispatcher, since they don't respect network connectivity constraints, and if the execution window is flexible, the job is run 2-3 minutes late even in ideal conditions (app is in the foreground, user is interacting with it, strong unmetered WiFi connection).

Thanks for the explanation, but it isn't as easy as you make it sound. The library relies a lot on the JobScheduler. If it needs to start a foreground service, then it must use the AlarmManager. The AlarmManager doesn't respect any requirements like a specific network condition. The library would need to check them manually like it does currently. But in this case a notification needs to pop up for a second in order to check the requirements.

The only option I see to implement this well is to not allow enforcing requirements. You need to manually check in your job if they're met. There may be some other constraints.

Right, but you don't actually need to execute the job inside the foreground service. You just need to keep a foreground service open. This helps with memory pressure, prevents app from going into app standby, and helps with network access if data saver is enabled. I'm not sure if this will help with Android 8.0 background restrictions though, need to test that.

Of course, not all jobs require foreground services, so this can be configurable, but disabled by default. You could also allow apps using the library to configure the notification title, message and maybe app icon. That way, only those people who need to use such a feature will do so, and others won't see a difference.

Of course, I get that adding more options is always a bit of a gamble, and as you've said this library was meant more for background jobs and not foreground jobs. Thanks for responding, and looking into it!

In a chat application you don't need to keep the connection running. Implement Firebase Cloud Messaging on your server. If your client is offline or unreachable, (i presume you use XMPP) then send the firebase message to the user's token. FCM is always running on the device whereas your service/job will not.

PS: Firebase Notifications is free.

@Dooks123 I non longer work in that company, but the purpose of that job wasn't to receive messages from the server - it was to send messages to the server. The purpose isn't to keep the connection open, it's to ensure reliable message upload, as quickly as possible, to the server.

You don't expect a messaging app to send a message 2 hours later, in the next doze mode window, when the device has an internet connection now, and can send the message to the server immediately.

Oh so basically to sync the data to the server? This does not sound like a normal chat app then. You need to be a little bit more specific about the app.

Because a messaging app is quite instant, you would handle the send message in real time with an open connection to the server (your device should be awake at this point). Receiving a message whilst connected should also be instant. If you exit the app, you close the connection. Even if the device goes into doze mode, it does allow for push notifications. So for your previous employer's purpose, the device is woken with a push notification, therefor you do not need long running services.

C2DM was there from the beginning, wasn't as good as GCM or today's FCM.

EDIT:
You can schedule android job for every 15 minutes or so, check if there is internet access, do your sync and close the connection. The OS will allow enough time to do small sync jobs, otherwise you should push a Sticky Notification (foreground service) to do your big data syncs, close the notification and allow the device to return to doze mode

Hi !
I develop an app to send notification with Cloud MQTT , I try it with JobSevice but i don't know how can i running a service when i receive message from a server ?..
Can you help me please !
Thank you

I'm running on Android 8.0 and on Oneplus 5 too, but it get killed too even I'm using foreground service.
I'm already trying to using job, trying this library, trying to pin the app, and using foreground service but no luck. The app still being killed, this famous issue called BgDetect on OnePlus to increase battery life.

The only way to save my app from getting killed is to white-listing my app on battery optimization.

Huawei has a similar implementation, https://stackoverflow.com/a/51140127/1876355 There should be a similar intent for OnePlus as well. There are more of these solutions whether to check if your app is white listed and to ask the user to add your app.

Wouldn't the regular "ignore battery optimizations" settings do the trick for OnePlus too?

val intent = Intent()
intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
activity.startActivity(intent)

No.

Each manufacturer has their own rolled battery management built into their OS.

https://stackoverflow.com/questions/47284735/prevent-android-battery-managers-from-stopping-my-application-service

This post above explains why Whatsapp, Facebook, Telegram, Line and a bunch more apps don't have this issue. The manufacturers add those apps to the white list from the start. Your app which is no-name compared to theirs must suffer the conditions.

ALSO CHECK https://stackoverflow.com/a/48641229/1876355
People attempt to gather a list of all Android flavors to implement a popup which tells the user to add your app to the white list.

Since this library will be deprecated in favor of WorkManger in the future, this feature won't be implemented.

Was this page helpful?
0 / 5 - 0 ratings