The point of the exercise is: keep the service alive, passing it from one activity to another.
- Activity A calls bindService() on service S;
- S.onBound() called;
- A.serviceConnection.onServiceConnected() is called;
- Activity A starts activity B;
- Activity B calls bindService() on service S;
- B.serviceConnection.onServiceConnected() is called;
5a: from onServiceConnected() activity B calls A.finish(); - Activity A is stopping, calling unbindService(S) from its onDestroy() method.
Expected behavior: Service S continues to exist happily until activity B calls unbindService()
Actual behavior:
- S.onUnbind() is called;
- S.onDestroy() is called;
- B.serviceConnection.onServiceDisconnected() is called;
thus destroying the link and contradicting the documentation.
Why? What am I missing?
Update: Solved. From http://developer.android.com/reference/android/app/Service.html:
A service can be both started and have connections bound to it. In
such a case, the system will keep the service running as long as
either it is started or there are one or more connections to it with
the Context.BIND_AUTO_CREATE flag.
Here's the code:
public class A extends Activity {
private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
private String serviceClassName;
private ServiceConnection feedConnection;
private Messenger feedMessenger;
private void bind(String argument) {
serviceClassName = TheService.class.getName();
Intent intent = new Intent(serviceClassName);
intent.putExtra(Keys.ACCOUNT, argument);
feedConnection = new FeedConnection();
if (!bindService(intent, feedConnection, Context.BIND_AUTO_CREATE)) {
throw new IllegalStateException("Failed to bind to " + argument);
}
logger.debug("bindService(" + serviceClassName + ") successful");
}
private void forward() {
Intent intentB = new Intent();
intentB.setClassName(B.class.getPackage().getName(), B.class.getName());
intentB.putExtra(Keys.SERVICE_CLASS_NAME, serviceClassName);
startActivity(intentB);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(feedConnection);
}
private class FeedConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
A.this.feedMessenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName className) {
A.this.feedMessenger = null;
logger.error("Crashed " + Integer.toHexString(hashCode()));
}
}
}
public class B extends Activity {
private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
private ServiceConnection feedConnection;
private Messenger feedMessenger;
private A activityA;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindFeed();
}
private void bindFeed() {
Intent startingIntent = getIntent();
String serviceClassName = startingIntent.getStringExtra(Keys.SERVICE_CLASS_NAME);
Intent intent = new Intent(serviceClassName);
feedConnection = new FeedConnection();
// FIXME: BIND_AUTO_CREATE flag is missing
if (!bindService(intent, feedConnection, 0)) {
throw new IllegalStateException("Failed to bind to " + serviceClassName);
}
logger.debug("bindService(" + serviceClassName + ") successful");
}
private class FeedConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
B.this.feedMessenger = new Messenger(service);
logger.debug("bound " + className);
// Finish the previous activity only after the service is bound
activityA.fileList();
}
@Override
public void onServiceDisconnected(ComponentName className) {
B.this.feedMessenger = null;
logger.error("Crashed " + className);
}
}
}