Tuesday, May 22, 2012

Service.onUnbind() called even if there are active clients?

The point of the exercise is: keep the service alive, passing it from one activity to another.




  1. Activity A calls bindService() on service S;

  2. S.onBound() called;

  3. A.serviceConnection.onServiceConnected() is called;

  4. Activity A starts activity B;

  5. Activity B calls bindService() on service S;

  6. B.serviceConnection.onServiceConnected() is called;
    5a: from onServiceConnected() activity B calls A.finish();

  7. 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:




  1. S.onUnbind() is called;

  2. S.onDestroy() is called;

  3. 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);
}
}
}




No comments:

Post a Comment