Notifications, Foreground Services, and Android 4.3
Two unannounced changes were added to Android 4.3 related to the behavior of foreground services and their respective notifications.
One is that the Notification
associated with a foreground
service
is immune to blocking via the “Show notifications” checkbox
for the app in Settings (or the equivalent facility
in App Ops). That Notification
– and perhaps others raised
by the foreground service – will always appear, even if
“Show notifications” is unchecked.
I can see why this will cause some justified consternation.
However, it appears to be tied to another change to the behavior
of startForeground()
, one that will have more users and developers
riled up.
You can roughly divide foreground services into three major buckets:
-
Those that are in the foreground solely to complete some transaction. The fact that a
Notification
appears during this transaction is unlikely to be an issue for users, as theNotification
is only there for a short time. -
Those that are in the foreground based upon a user operation, such as pressing “play” in a music player. In these cases, the user should understand why the
Notification
is there, and theNotification
can help get the user back to a spot on the app to reverse their decision (e.g., press “stop” to stop the music). -
Those that are trying to live forever.
The everlasting service has been an anti-pattern in Android pretty
much since Android 1.0. The T-Mobile G1 was released in October 2008;
the first task killers showed up in a month or two. Particularly at
that time, the memory footprint of an app was relatively large compared
to the available system RAM, and so a service-laden process would
have an outsized effect on multitasking. As the years progressed,
Android has taken an increasingly hard line with such apps, but
has had the notion of a “foreground service” for cases such as the
first two buckets above. The exchange for being relatively “unkillable”
is that the app had to display a Notification
, to inform the user
about the everlasting service, and ideally allowing the user to
take steps to stop that service gracefully from within the app.
The expectation was that the Notification
requirement would help
limit the foreground services to apps that truly need it in the eyes
of developers and users alike (e.g., third-party VOIP clients).
However, give developers 2.54cm, and they’ll take 1.61km.
I filed an issue last year,
pointing out what had become common knowledge on StackOverflow:
that startForeground()
could be passed an invalid Notification
.
The service would gain foreground privileges, without the
user having a Notification
informing them of this effect. Google
probably knew about this issue already – leastways, they never
acknowledged the one that I filed – and in Android 4.3, they
have taken corrective action.
Specifically, to quote Dianne Hackborn:
…what we ultimately did in 4.3 is have the system put up its own notification when it detects this so that the user is aware of what is going on and the app doesn’t have incentive any more to do this.
This is better than their original plan, which was to start
raising an exception, thereby crashing apps that supplied
invalid an Notification
to startForeground()
.
So, now users will see extra notifications, tied to foreground services in apps whose developers tried to cheat the system.
The comments on Ms. Hackborn’s post contain lots of spluttering, as
developers who employ hacks like this tend to splutter when their
hack is no longer effective. The fact that such notifications can
no longer be blocked by users will cause user frustration, as users
have no ability to opt into having a foreground service without
such a notification anymore. And while I haven’t tested the 4.3
change, I suspect that it affects all foreground services, and one
could argue that there should be a de minimis exception (e.g.,
only display the Notification
if the process consumes >1% of
system RAM).
That being said, if you were using this hack, stop, and start figuring out what you are going to do about it.
For example:
-
If you were using a foreground service because you were too lazy to fix some bug in your app caused by your process being terminated and later restarted (e.g.,
START_STICKY
), fix the bug. -
Consider making foreground behavior optional. Since the decision of marking a serivce as a foreground service is made in Java, not the manifest, it is relatively easy for you to add a checkbox to your app settings to allow the user to indicate whether the benefits of foreground-ness are worth the cost of having the
Notification
around. -
Consider making the service optional. For example, some developers insist on writing services to listen for
ACTION_SCREEN_OFF
and/orACTION_SCREEN_ON
, in an attempt to optimize some behavior. I suspect that few have done much analysis to determine if this actually is an optimization, in part because doing such analysis is rather difficult to peform accurately without specialized equipment. Once again, though, you can elect to give the user control over this, giving them a checkbox to control whether you monitor for these events or fall back to more traditional behavior. -
There is no need to have more than one simultaneous foreground service. One service can have multiple threads to do disparate operations.