Our app can connect to a HVAC device, and need to sample its state every 10 seconds.
The HVAC device expose a Modbus interface and CANNOT buffer samples. We ACTUALLY need to wake up the service every 10 seconds. A typical sample session last from 1 hour to a few hours. (There are various connection types/cables/bridges: OTG + RS232 USB converter, TCP trough a RS232/TCP bridge, Bluetooth classic and BLE through RS232/BT or BLE bridges.)
Of course the user should be able to turn off the display to save battery, and to navigate to other apps, while the sampling session is taking place.
Until Nougat 7.0 our working solution was:
- Start an
IntenService
, then set it toForeground
Service - Bound to that service, so it's also a
Bounded
Service (to stop it, propagate interaction with the HVAC device from the GUI, etc.) - Acquire a
PowerManager.PARTIAL_WAKE_LOCK
- Use a
Thread.Sleep()
for the 10 seconds cycle, but also to manage timeouts in the transport implementation of the various connections
Should I post the code? The relevant parts are most common boilerplate.
Despite having no documented guarantees, Thread.Sleep()
with a PowerManager.PARTIAL_WAKE_LOCK
in a Foreground Service was working perfectly until Nougat 7.0
On our testing Oreo device, both compiling targeting API 26 and 23 (as our previous released version), while not interacting with the app activities (turned off display, other apps in foreground etc.), the service does not wake up, it sleeps until the first interaction with the app.
To be able to start a foreground service on Oreo targeting API 26, I implemented Bikram Pandit's solution from Android O - Old start foreground service still working?. Nothing changed.
I asked for a Battery Optimizations exception, with ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
(as descripted in https://developer.android.com/training/monitoring-device-state/doze-standby#whitelisting-cases ), and granted it. Nothing changed. (And the app was working without it until Nougat 7.0)
The documentation about Oreo's Background Execution Limits, https://developer.android.com/about/versions/oreo/background, states that Bounded Services or Foreground Services should NOT be subjected to these limits. And yet the service doens't wake up.
AlarmManager
cannot be used, since from Oreo it can fire at most every 9 minutes, even with setAndAllowWhileIdle()
nor setExactAndAllowWhileIdle()
. (Again in https://developer.android.com/training/monitoring-device-state/doze-standby#whitelisting-cases)
I also tried using a wait(sleepTime)
instead of the Thread.sleep()
: it's even worse, it doesn't even wake up when restarting interacting.
I stopped short before re-implementing as a JobIntentService
or using JobScheduler
/JobService
, to reframe every sample as a job, but:
- IMPORTANT: I would still need the
Thread.Sleep()
in some of the transport level connection implementations, will that wake up during a job? - Would
JobScheduler
actually allow to run jobs every 10 seconds, or would it bundle them together on Oreo? We actually need a sample every 10 seconds. We probably could relax to 1 minute, but not any larger. - Can I retain very complex state (my ModbusDevice object) in
JobService
, to share it among all those jobs or there would be limitations?
Again, should I post some code? It's most common boilerplate.