Long-Running Background Tasks: A Survival Guide
The War on Background Processing
It Continues!
- Attempts by Google to reduce battery consumption and other perceived waste
- War ongoing since 4.4, heated up with Doze mode/app standby in 6.0
- Casualties: apps that relied upon now-broken techniques, users of said apps
Types of Background Work
At Least, Conventional Types
-
Foreground-Triggered Work
- Transactional (e.g., file download)
- Ongoing (e.g., media player)
-
Background-Triggered Work
- Periodic (e.g., scheduled server sync)
- Push messages (e.g., C2DM/GCM/FCM)
- System broadcasts as triggers (e.g., after app installs)
Historical Thread Recommendations
(No, That's Not "Hysterical")
- Under 1ms? Use the main application thread
- Over 1ms? Use any form of background thread
- Over 1 second? Use a service
- Over 15 seconds, or background-triggered? Wakelock, WiFi-lock, and foreground service
- Good news! Follow this advice, and your code still works!
Foreground Foes
- Do not want to clutter up the status bar
- Do not want to clutter up the notification shade
- Do not want the user to know that the service is running
Oreo Started Services
Gone in 60 Seconds
- Limited to "several minutes"... where "several" seems be "one"
- After that, service will be stopped, akin to
stopService()
- Does not directly terminate your process, but dramatically increases the odds that it will terminate shortly
- Affects
targetSdkVersion 26
... and anyone the user chooses to blame
The Foreground Workaround
- Simple solution: make your service be a foreground service
- Foreground started ("unbound") services can run normally
- Does not make the "foreground foes" happy, but... ¯\_(ツ)_/¯
No Ouroboros
A Snake Eating Its Tail, To Save You a Wikipedia Search
- With no running services, your process is considered "cached"
- While cached, you cannot start other services using
startService()
Promises, Promises
Because You Never Lie to Android, Right?
- Call
startForegroundService()
instead
- Expectation: you will call
startForeground()
from the service, within the ANR timeout period
- Works even from cached processes
- And, if you just happen to get your work done in a few seconds, and never call
startForeground()
... ¯\_(ツ)_/¯
About the Foreground Notification
You Can Run, But You Can't Hide
- Using a minimum-importance channel hides your notification... but shows a system one, indicating that you are running
- Using any higher importance shows your own notification
Get a Job
(IntentService
)
JobIntentService
, found in Support Library 26.0.0 and higher
- On Android 7.1 and older, behaves like
IntentService
- On Android 8.0+, uses
JobScheduler
- Gives you a larger but undocumented window for work (10 minutes?)
- Handles wakelock, akin to now-deprecated
WakefulBroadcastReceiver
Implicit Broadcast Ban
Fighting Fire with More Fire
targetSdkVersion 26
apps cannot receive implicit broadcasts via manifest-registered receivers
- Massive expansion of formerly uncommon limitation (e.g.,
ACTION_BATTERY_CHANGED
)
- Rationale: process churn
- Select system implicit broadcasts are whitelisted, as are self-broadcasts protected by
signature
permissions
Triggering Work Sans Implicit Broadcasts
This Jocular Sub-Head Available for Rent, Inquire Within
- Find another way to get this information, via polling and
JobScheduler
- Use a foreground service and a dynamic receiver
- Keep your
targetSdkVersion
below 26 until you can figure out a better workaround
Questions?
https://commonsware.com/presos/androidSummit2017