I have a Foreground Service which I start and stop with a Toggle Button in an Activity. To keep track if the Service is running or not I have used a variable which I store in SharedPreference
.
Here is the flow
When a user enables toggle button
startService() start background work
makeForeGround()
bindService() to get update
When a user disables toggle button
unbindService() to stop getting updates
stopForeGround()
stopService() stop background work
When a user leaves the Activity
unbindService(); To stop getting the update.
But the service is running for background work
When a user reenters the Activity
if(Service is running)
Show toggle button state as enabled
bindService()
I have achieved all these.
Only problem I face is this:
Sometimes my Service gets killed by OS. In that case, I have no way to
update My SharedPreference
variable. Hence no way to sync my Toggle
button with Service.
How do I get notified that the service has got killed by OS. (Because onDestroy() is not getting called in this case)? I have also gone through How to check if a service is running on Android?, but sadly the solutions and methods provided are deprecated.
Inside the service, usually in onStartCommand() , you can request that your service run in the foreground. To do so, call startForeground() . This method takes two parameters: a positive integer that uniquely identifies the notification in the status bar and the Notification object itself.
NO MORE STARTSERVICE - The new context. startForegroundService() method starts a foreground service but with a implicit contract that service will call start foreground within 5 second of its creation. You can also call startService and startForegroundService for different OS version.
Activity or dialog appears in foregroundWhen the covered activity returns to the foreground and regains focus, it calls onResume() . If a new activity or dialog appears in the foreground, taking focus and completely covering the activity in progress, the covered activity loses focus and enters the Stopped state.
You should consider approaching your problem from a different direction.
Trying to infer when the system has killed your Service
is a problematic. Apps (Services
) are not notified when they are terminated, therefor detecting this condition is difficult. However, finding out when a Service
has started is trivial (onCreate()
). This is a clue to the right approach.
It's clear that you understand that the proper way to exchange information between an Activity
and a Service
is via a binding. This is the other critical piece of the solution.
SOLUTION
You should expand the lifetime of your Service
. Right now, it appears that you create the Service
for the express purpose of conducting a long-running task. The Service
is created when you want the task to start, and terminates as soon as it ends. In other words, the scope of the Service
and the "task" are the same.
Instead, you should make the "task" a child of the Service
; the Service
should exist for somewhat longer than the "task". In particular, the Service
should be available whenever the Activity
is started (if for no other reason than to communicate information about the state of the "task" to the Activity
).
Whenever the Activity
starts, it should bind to the Service
, and register a listener with the Service
. The Service
calls this listener whenever A.) the listener is newly registered, or B.) the "task" changes states (starts or stops). Thus, the Activity
is always informed instantly of state changes, so long as it is bound to the Service
.
When the Activity
wants the "task" to change states, it should inform the Service
via the binding (or possibly a startService()
call, even though the Service
is already running). The Service
would then start/stop the background thread (or whatever it is you do to accomplish the "task"), and notify any listeners.
If the Service
has been running the "task" for some time, and the Activity pops up all of a sudden, then the Service
clearly knows the "task" exists, and the Activity
will get that information via the listener, as soon as it binds.
When the Activity
stops, it should clean up by unregistering its listener, and unbinding from the Service
.
This approach obviates the need for SharedPreferences
, or to detect when the Service
has stopped. Whenever the Service
is created, obviously the "task" is not running, so the Service
will always know the correct answer to give to the Activity
. And since the Service
must be created before it can bind to the Activity
, there is no order-of-operations problem.
I redesigned. Now the flow is
Entering the Activity
bindService()
Leaving the Activity
unbindService()
Enable Toggle
startService()
Disable Toggle
stopService()
For that, I am using an int variable SERVICE_START_COUNTER
in the service and increase its value in onStartCommand(). And I receive the SERVICE_START_COUNTER
when the Activity binds to service. Based on the counter value I can differentiate if the service is running or not
public boolean isServiceRunning(int SERVICE_START_COUNTER)
{
return SERVICE_START_COUNTER == 0 ? false : true;
}
And based on this value I have written a method which sync-up the UI.
public void syncUI(boolean isServiceRunning)
{
setToggleState(isServiceRunning);
setAniamtionState(isServiceRunning);
}
1) Enter the Activity for the first time (Toggle is still disabled)
onBind() // SERVICE_START_COUNTER = 0
2) Enter the Activity for the first time (User enables the toggle)
onBind() // SERVICE_START_COUNTER = 0
onStartCommand() // SERVICE_START_COUNTER = 1
3) Re-enter the Activity for the second time (Toggle is enabled)
onBind() // SERVICE_START_COUNTER = 1
4) Service is killed by OS. Re-enter the Activity
onBind() // SERVICE_START_COUNTER = 0
Since binding takes time (because onServiceConnected()) is called asynchronously. Hence the Result might not be available right away. For this, I first check the status of service in the Splash Activity and update a similar counter in a Utility class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With