WorkManager, App Widgets, and the Cost of Side Effects
WorkManager
is “a nice piece of kit”. It has a
fairly clean and easy API and hides a lot of the complexity of scheduling
background work that is not time-sensitive.
However, it has side effects.
To be able to restart your scheduled work after a reboot, WorkManager
registers
an ACTION_BOOT_COMPLETED
receiver named androidx.work.impl.background.systemalarm.RescheduleReceiver
.
To be a good citizen, WorkManager
only enables that receiver when you have
relevant work and disables it otherwise. That way, your app does not unnecessarily
slow down the boot process if there is no reason for your app to get control at
boot time.
However, enabling and disabling a component, such as a receiver, triggers an
ACTION_PACKAGE_CHANGED
broadcast. Few apps directly have any code that watches
for this broadcast, let alone would be harmed by having that broadcast be sent
more times that might otherwise be necessary.
App widgets, though, are affected by ACTION_PACKAGE_CHANGED
. Specifically,
ACTION_PACKAGE_CHANGED
triggers an onUpdate()
call to your AppWidgetProvider
.
That too may not be a problem for most app widgets. Ideally, your AppWidgetProvider
makes no assumptions about when, or how frequently, it gets called with onUpdate()
.
This bug report points out one
area where this is a problem: with an AppWidgetProvider
scheduling work
in onUpdate()
. The flow then becomes:
- A “regular”
onUpdate()
call comes in - Your
AppWidgetProvider
schedules some work withWorkManager
WorkManager
enablesRescheduleReceiver
- That triggers
ACTION_PACKAGE_CHANGED
, which triggers anonUpdate()
call - Your
AppWidgetProvider
schedules some work withWorkManager
again WorkManager
eventually gets through those two pieces of workWorkManager
disablesRescheduleReceiver
, since it is no longer needed- That triggers
ACTION_PACKAGE_CHANGED
, which triggers anonUpdate()
call - Your
AppWidgetProvider
schedules some work withWorkManager
WorkManager
enablesRescheduleReceiver
- And we’re in an infinite loop
The recommendation from Google is
to avoid unconditionally scheduling work with WorkManager
from onUpdate()
.
Instead, only do it if you know that the work is needed and that it is safe to do
so, meaning that you will not get into the infinite loop.
That advice may be difficult for some to implement.
Beyond that, WorkManager
is the one causing the side effect. In principle,
WorkManager
should provide options to avoid or manage that side effect.
Side effects from libraries are “negative externalities”, to use an economics term.
They are like pollution: the developer of the library does not bear the cost, whereas
users of the library do, and sometimes users of the apps using the library do.
If your library has the potential for side effects, be sure to document those
side effects and, to the greatest extent possible, give ways for developers
to manage or mitigate the side effects.
IOW, library developers need to “give a hoot — don’t pollute”.