When is a started and bound Service destroyed?

AndroidAndroid Service

Android Problem Overview


I was going through the services documentation in android when I noticed two contradicting points:

In the services document it is specified in Managing the Lifecycle of a Service

> These two paths are not entirely separate. That is, you can bind to a > service that was already started with startService(). For example, a > background music service could be started by calling startService() > with an Intent that identifies the music to play. Later, possibly when > the user wants to exercise some control over the player or get > information about the current song, an activity can bind to the > service by calling bindService(). In cases like this, stopService() or > stopSelf() does not actually stop the service until all clients > unbind.

But in the document about bound services in Managing the Lifecycle of a Bound Service

> However, if you choose to implement the onStartCommand() callback > method, then you must explicitly stop the service, because the service > is now considered to be started. In this case, the service runs until > the service stops itself with stopSelf() or another component calls > stopService(), regardless of whether it is bound to any clients.

It may be me but I think the statements are contradictory.Could anyone please clarify...

Android Solutions


Solution 1 - Android

Agree that the documentation could be clearer. What they are trying to say is:

  • If you call startService(), then the service will keep running unless and until you call stopSerivce() (or stopSelf() from within the service)
  • If you call bindService(), then the service will keep running unless and until you call unbindService()
  • Therefore, if you call both startService() and bindService(), then the service will keep running until you call both stopService and unbindService(). Neither on its own will stop the service.

Created a very simple Activity and Service and ran the following sequences of start/stop/bind/unbind. I observed that the calls gave the following results.

bind-unbind

bindService() caused:
	onCreate()
	onBind()
unbindService() caused:
	onUnbind()
	onDestroy()

start-bind-unbind-stop

startService() caused:
	onCreate()
	onStartCommand()
bindService() caused:
	onBind()
unbindService() caused:
	onUnbind()
stopService() caused:
	onDestroy()

start-bind-stop-unbind

startService() caused:
	onCreate()
	onStartCommand()
bindService() caused:
	onBind()
stopService() caused:
	-- nothing
unbindService() caused:
	onUnbind()
	onDestroy()
	

bind-start-stop-unbind

bindService() caused:
	onCreate()
	onBind()
startService() caused:
	onStartCommand()
stopService() caused:
	-- nothing -- still running
unbindService() caused:
	onUnbind()
	onDestroy()
		

bind-start-unbind-stop

bindService() caused:
	onCreate()
	onBind()
startService() caused:
	onStartCommand()
unbindService() caused:
	onUnbind()
stopService() caused:
	onDestroy()

As you can see, in each case where both bind and start were called, the service kept running until both unbind and stop were called. The sequence of unbind/stop is not important.

Here is the example code that was called from separate buttons in my simple test app:

public void onBindBtnClick(View view) {
	Intent intent = new Intent(MainActivity.this, ExampleService.class);
	bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}

public void onUnbindBtnClick(View view) {
	if (serviceIsBound) {
        unbindService(serviceConnection);
        serviceIsBound = false;
    }
}

public void onStartBtnClick(View view) {
	Intent intent = new Intent(MainActivity.this, ExampleService.class);
    startService(intent);
}

public void onStopBtnClick(View view) {
	Intent intent = new Intent(MainActivity.this, ExampleService.class);
    exampleService.stopService(intent);
}

Solution 2 - Android

Actually, both paragraphs complement each other (although their wording might be misguiding), and both paragraphs are consistent with the image from the documentation. Let's have a look:

> These two paths are not entirely separate. That is, you can bind to a service that was already started with startService(). For example, a background music service could be started by calling startService() with an Intent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind.

The quintessence is: If you start a service, then bind a client to it, then try to stop it, the service is not stopped (destroyed) before all clients unbind. The second paragraph does not contradict, it refines this statement.

> However, if you choose to implement the onStartCommand() callback method, then you must explicitly stop the service, because the service is now considered to be started. In this case, the service runs until the service stops itself with stopSelf() or another component calls stopService(), regardless of whether it is bound to any clients.

This means: A started and bound service runs even if no clients are bound to it until it is explicitely stopped. Granted, the wording might probably be a bit clearer on this. The lifecycle diagram given in the documentation however shows this (and I am pretty sure I already observed this in "real-life", although I am currently have no direct example on top of my head):

Lifecycle for started and bound services

Solution 3 - Android

Yep, it works. I want to complete with a sample code :

I had to make an app with a service started by an activity, the activity have to call some methods in the service, the service have to run in background even if the activity were killed, and when the activity restarts, it haven't to restart the service if it is running. I hope it will help you, you can see how does it work with the Log. So that is the code :

 public class MyActivity extends Activity{

	private MyService myService;
	private boolean mIsBound = false;

	private ServiceConnection mConnection = new ServiceConnection() {

		public void onServiceConnected(ComponentName className, IBinder binder) {
			MyService.MyBinder b = (MyService.MyBinder) binder;
			myService = b.getService();
			mIsBound = true
			//Do something
			// Here you can call : myService.aFonctionInMyService();

		}
		public void onServiceDisconnected(ComponentName className) {
			// Do something
			mIsBound = false;
		}
	}



	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		
		//Checked if my service is running
		if (!isMyServiceRunning()) {
			//if not, I start it.
			startService(new Intent(this,MyService.class));
		}
	}

	private boolean isMyServiceRunning() {
		ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
		for (RunningServiceInfo service : manager
				.getRunningServices(Integer.MAX_VALUE)) {
			if (MyService.class.getName().equals(
					service.service.getClassName())) {
				return true;
			}
		}
		return false;
	}

	@Override
	protected void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		doBindService();
	}




	//Connection to the Service
	private void doBindService() {
		bindService(new Intent(this,MyService.class), mConnection,
				Context.BIND_AUTO_CREATE);
	}

	// Disconnection from the service
	private void doUnbindService() {
		if (mIsBound) {
			// Detach our existing connection.
			unbindService(mConnection);
		}
	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		doUnbindService();
		super.onPause();
	}

}


public class MyService extends Service{


	public static String Tag = "MyService";
    private final IBinder mBinder = new MyBinder();

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub		
		super.onCreate();
		Log.d(Tag, "onCreate()");
		
	}

	public class MyBinder extends Binder {
		public LocationService getService() {
			return LocationService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Log.d(Tag, "onBind()");
		return mBinder;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		// TODO Auto-generated method stub
		Log.d(Tag, "onUnBind()");
		return super.onUnbind(intent);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		Log.d(Tag,"onStartCommand()");
		
		return START_STICKY;
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		
		Log.d(Tag, "onDestroy");
		super.onDestroy();
	}

	public void aFonctionInMyService(){
		//Do Something
	}

}

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
QuestionanzView Question on Stackoverflow
Solution 1 - AndroidChuck KrutsingerView Answer on Stackoverflow
Solution 2 - AndroidStephanView Answer on Stackoverflow
Solution 3 - AndroidCocoricoView Answer on Stackoverflow