When is a started and bound Service destroyed?
AndroidAndroid ServiceAndroid 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):
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
}
}