Why does my Android service get restarted when the process is killed, even though I used START_NOT_STICKY?

AndroidAndroid Service

Android Problem Overview


My app uses a pattern where I start a service with Context#startService() as well as bind to it with Context#bindService(). This is so that I can control the lifetime of the service independently from whether any clients are currently bound to it. However, I noticed recently that whenever my app is killed by the system, it soon restarts any services that were running. At this point the service will never be told to stop, and this is causing battery drain whenever it happens. Here's a minimal example:

I found someone with a similar issue here, but it wasn't ever diagnosed or solved.

Service:

@Override
public void onCreate() {
	Toast.makeText(this, "onCreate", Toast.LENGTH_LONG).show();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	return START_NOT_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
	return new Binder();
}

Activity:

@Override
protected void onStart() {
	super.onStart();
	Intent service = new Intent(this, BoundService.class);
	startService(service);
	bindService(service, mServiceConnection, 0);
}

@Override
protected void onStop() {
	unbindService(mServiceConnection);
	Toast.makeText(this, "unbindService", Toast.LENGTH_SHORT).show();
	super.onStop();
}

To test it, I launched the app, which started the service and bound to it. Then I backed out of the app, which unbinds (but leaves the service running). Then I did

$ adb shell am kill com.tavianator.servicerestart

and sure enough, 5 seconds later, the "onCreate" toast appears, indicating that the service started again. Logcat shows this:

$ adb logcat | grep BoundService
W/ActivityManager(  306): Scheduling restart of crashed service com.tavianator.servicerestart/.BoundService in 5000ms
I/ActivityManager(  306): Start proc com.tavianator.servicerestart for service com.tavianator.servicerestart/.BoundService: pid=20900 uid=10096 gids={1028}

If I replace the startService() pattern with BIND_AUTO_CREATE, the problem doesn't occur (even if I crash the app while it's still bound to the service). It also works if I never bind to the service. But the combination of start, bind, and unbind seems to never let my service die.

Using dumpsys before killing the app shows this:

$ adb shell dumpsys activity services com.tavianator.servicerestart
ACTIVITY MANAGER SERVICES (dumpsys activity services)
  Active services:
  * ServiceRecord{43099410 com.tavianator.servicerestart/.BoundService}
    intent={cmp=com.tavianator.servicerestart/.BoundService}
    packageName=com.tavianator.servicerestart
    processName=com.tavianator.servicerestart
    baseDir=/data/app/com.tavianator.servicerestart-2.apk
    dataDir=/data/data/com.tavianator.servicerestart
    app=ProcessRecord{424fb5c8 20473:com.tavianator.servicerestart/u0a96}
    createTime=-20s825ms lastActivity=-20s825ms
    executingStart=-5s0ms restartTime=-20s825ms
    startRequested=true stopIfKilled=true callStart=true lastStartId=1
    Bindings:
    * IntentBindRecord{42e5e7c0}:
      intent={cmp=com.tavianator.servicerestart/.BoundService}
      binder=android.os.BinderProxy@42aee778
      requested=true received=true hasBound=false doRebind=false

Android Solutions


Solution 1 - Android

Look to this document section: Service lifecycle changes (since 1.6)

updated

after some investigations (have created project, run described command step by step, etc) i found that code is working exactly as described in "Service lifecycle changes"

what I found:
adb shell am kill com.tavianator.servicerestart kills nothing
(try adb shell , then in shell am kill com.tavianator.servicerestart
You will be faced with error message Error: Unknown command: kill)

so,
run your application,
run adb shell
in shell run ps command
find PID number of Your app
in shell run command kill <app_xx_PID>
where is Your PID number
repeat killing steps for service (if it is running in its own process)
check if service is running (should not), restarted after 5-7 sec
update
one solution (not enough good, but usable in some cases) is stopSelf() e.g.:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
	stopSelf();
    return START_NOT_STICKY;
}


update
updated solution

void writeState(int state) {
	Editor editor = getSharedPreferences("serviceStart", MODE_MULTI_PROCESS)
			.edit();
	editor.clear();
	editor.putInt("normalStart", state);
	editor.commit();
}

int getState() {
	return getApplicationContext().getSharedPreferences("serviceStart",
			MODE_MULTI_PROCESS).getInt("normalStart", 1);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	if (getState() == 0) {
		writeState(1);
		stopSelf();
	} else {
		writeState(0);
		Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
	}
	return START_NOT_STICKY;
}

Why service get restrted when the process is killed?

According to this document:

> When a service is started, it has a lifecycle that's independent of > the component that started it and the service can run in the > background indefinitely, even if the component that started it is > destroyed. As such, the service should stop itself when its job is > done by calling stopSelf(), or another component can stop it by > calling stopService().
Caution: It's important that your > application stops its services when it's done working, to avoid > wasting system resources and consuming battery power. If necessary, > other components can stop the service by calling stopService(). Even > if you enable binding for the service, you must always stop the > service yourself if it ever received a call to onStartCommand()

from other hand document says:

> START_NOT_STICKY - If the system kills the service after onStartCommand() returns, do not recreate the service, unless there > are pending intents to deliver. This is the safest option to avoid > running your service when not necessary and when your application can > simply restart any unfinished jobs.

So, after reading this document and some experiments i think system treats manually killed services as unfinished (crashed: @see W/ActivityManager(306): Scheduling restart of crashed service) and restarts it despite of value returned by onStartCommand.


stopSelf() or stopService() - no restarts, why not if job done ?

Solution 2 - Android

I believe the problem here is that for a Service that was started by calling startService(), the internal accounting for its bindings (affected by calls to bindService()/unbindService()) are somehow preventing the service to be brought down correctly after it is scheduled for restart when the process is killed.

Depending on your preference, it seems that there are three alternatives to avoid this issue (you mentioned the first two already in your question):

  • Use startService()/stopService() only, do not use bindService()/unbindService() at all.

  • Use bindService()/unbindService() only (with BIND_AUTO_CREATE), do not use startService()/stopService() at all.

  • If you need startService()/stopService() and bindService()/unbindService(), override the onUnbind() method in your service and call stopSelf() from it:

     @Override
     public boolean onUnbind(Intent intent) {
         stopSelf();
         return super.onUnbind(intent);
     }
    

From my testing:

  • Using the first alternative will print this line in the log:

    > W/ActivityManager( nnn): Scheduling restart of crashed service xxxx in 5000ms

    but the service will not be really restarted after that.

  • The second alternative will cause your service to be destroyed after you unbind (in the Activity's onStop() in your example).

  • The third alternative will basically make your code behave like the second alternative (without needing to change it by much).

Hope this helps!

Solution 3 - Android

How about reconsidering your pattern?

The START_NOT_STICKY flag appeared at some point during Android evolution (after 1.6?). Most probably, you are facing some subtle regression in service life cycle, which was introduced at that time. So, even if an equally subtle solution is found for now - and magically verified to work on all possible devices - it will not necessarily work under new Android versions.

Anyway, that life cycle pattern looks unusual, and that might be the very reason for your running into this exotic problem.

Let's consider two cases.

  1. Normal (?). Some activity starts the service. Soon after that it gets bound, used, unbound - then what? Stopped somehow? How come there's no significant battery drain in this case?

  2. Abnormal. Service killed at some (random?) point. When restarted, it implements some wrong assumption and stays active, doing something that drains the battery?

It all looks rather strange.

I would analyze the concurrent processes in your product. All significant cases. Some diagrams, etc. Most probably, as a result, the very need for such a pattern would be eliminated.

Otherwise, it looks like any workaround will be a fragile hack.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTavian BarnesView Question on Stackoverflow
Solution 1 - Androidra.View Answer on Stackoverflow
Solution 2 - AndroidJoeView Answer on Stackoverflow
Solution 3 - Androidfull.stack.exView Answer on Stackoverflow