One of my apps has a backgrouod service that uses the START_STICKY
return code from onStartCommand
to automatically restart when the system kills it.
It seems that this is no longer working on Android KitKat.
Is there any solution for this ?
Should I be doing something different on Kitkat to keep the service running ?
Note: There is a similar discussion on the Android-Devlopers group about swiping the app from the recent apps list behaves. Could this two issues be related ? https://groups.google.com/forum/#!topic/android-developers/H-DSQ4-tiac
Edit: Saw that there are open bugs on Android issue tracker:
https://code.google.com/p/android/issues/detail?id=63793 https://code.google.com/p/android/issues/detail?id=63618
Edit2: The same happens even if service is running using startForeground
, in a separate process and with the flag android:stopWithTask="false"
in the AndroidManifest.xml file...
Edit3: More related bugs on Android issue tracker:
https://code.google.com/p/android/issues/detail?id=62091 https://code.google.com/p/android/issues/detail?id=53313 https://code.google.com/p/android/issues/detail?id=104308
Is there some sort of workaround to get the previous behavior ?
The problem here appears to not to occur on AOSP based ROMs. That is, I can easily recreate this on a CyanogenMod 11 based ROM, but on an AOSP ROM (and on an Emulator), START_STICKY behaves exactly as I'd expect. That said, I am seeing reports from folks on Nexus 5's that appear to be seeing this behavior, so perhaps it is still an issue in AOSP.
On an emulator and on an AOSP ROM, I see the following from a logcat when I do a 'kill 5838' against the process (as I'd expect):
I see the same restart behavior if I end the task by 'swiping' from the recent tasks list. So this is all good - it means that the core AOSP code is behaving as it has in previous levels.
I am looking at the Cyanogenmod service code to try and figure out why things aren't getting scheduled for restart - no luck yet. It appears that it should reschedule it. Cyanogenmod uses a service map which AOSP doesn't - but unclear whether that is an issue or not (doubtful) https://github.com/CyanogenMod/android_frameworks_base/blob/cm-11.0/services/java/com/android/server/am/ActiveServices.java#L2092
A rather hackish workaround you can do is to use a similar mechanism as your onTaskRemoved AlarmService to enable an alarm for X minutes later. Then every few minutes while your app is up and running, you can reset the alarm - so it only goes off if things really have been killed and not restarted. This isn't foolproof - using a Handler gives you uptime vs the alarm service which uses realtime, so it's possible for your alarm to trigger even though it was set at a longer time than your 'reset' handler. But if you set an intent extra you can chose to ignore the onStartCommand if your service was already up and running, turning this into a noop.
I'm not a fan of the following hack at all - but it shouldn't do any real harm. If the user does an explicit Force Close, then the alarm manager will destroy any alarms set so that the service won't restart (which is what the user wants).
First, create a helper method that will set an alarm for 20 minutes which will cause onStartCommand to be triggered for your service. Every 2 minutes have a Handler which will reset the 20 minute alarm. If the handler runs within the realtime 20 minutes, the alarm will never go off. The handler isn't guaranteed to run though if the device is asleep (which is good).
In your onCreate you can call this method. Also - in your onStartCommand, be sure to ignore this if your service is already up and running. EG:
Seems that this is a bug present in Android 4.4, got around it with the following:
Found this answer from this post
This is not a 100% working solution but it's the best so far as it almost completely eliminates the problem. So far I integrated this solution along with overriding
onTaskRemoved
(See this answer) and a keep-alive notification (See this answer). Additional answers are very appreciated !After further investigation, it seems that the bug already exists in Jelly Bean and looks like there is a solution for that (At least in my case that seems to work. will keep on testing and update the answer if required).
From what I observed this only happens with services that receive broadcasts set by
AlarmManager
.To reproduce the bug follow these steps:
startForeground
for that) from within the appUsing
adb shell dumpsys >C:\dumpsys.txt
you can monitor the state of the service between the different steps. (look forProcess LRU list
in the dumpsys output) on steps 2 and 3 you will see something like this:Specifically, notice the
F/S/IF
and the(fg-service)
that indicate the service is running as a foreground service (more details on how to analyze the dumpsys at this link: https://stackoverflow.com/a/14293528/624109).After step 4 you will not see your service in the
Process LRU list
. Instead, you can look at the device logcat and you will see the following:What seems to be causing that behavior is the fact that the received broadcast takes the service out of its foreground state and then killed.
To avoid that, you can use this simple solution when creating your
PendingIntent
for theAlarmManager
(Source: https://code.google.com/p/android/issues/detail?id=53313#c7)Pay attention to the following steps:
FLAG_RECEIVER_FOREGROUND
If you leave any of those steps out it will not work.
Note that the
FLAG_RECEIVER_FOREGROUND
was added on API 16 (Jelly Bean) so it makes sense that this is when the bug first appeared...Most likely that KitKat is just more aggressive when it comes to killing processes and this is why it was emphasized with KitKat, but looks like this was already relevant on Jelly Bean.
Note 2: Notice the details in the question about the service configuration - running in a separate process, as a foreground service, with endWithTask set to false in the manifest.
Note 3: The same thing happens when the app receives the
android.appwidget.action.APPWIDGET_CONFIGURE
message and shows a configuration activity for a new widget (Replace step 4 above with creating a new widget). I found that only happens when the widget provider (the receiver that handlesandroid.appwidget.action.APPWIDGET_UPDATE
) is set to run on a different process than the activity process. After changing that so both the configuration activity and the widget provider are on the same process, this no longer happens.i found this simple trick to solve this problem without using AlarmManager.
create a broadcast receiver that listens broadcast everytime
onDestroy()
method in service is called:add customized broadcast intent to your manifest
then, send broadcast from
onDestroy()
, probably like this:call
onDestroy()
fromonTaskRemoved(Intent intent)
this trick will restart your service everytime user close service from both task manager and force close from settings, i hope this will help you too