When should I use unbindService(), and how should I use it properly to unbind from a remote service that is using an AIDL interface?

Junseok Lee picture Junseok Lee · Jul 11, 2011 · Viewed 7.5k times · Source

I'm writing a simple music player, and I've created a playback service which implements an AIDL interface to bind with the clients, one a simple track browser and the other an even simpler player activity. The service controls the MediaPlayer object while the two activities use ServiceConnections to obtain connections to the service.

This is included in the onStart() methods of both activities:

@Override
public void onStart()
{
  super.onStart();
  Intent i = new Intent(this, PureService.class);
  startService(i);
  bindService(i, mConnection, 0);
}

I did this so that the service would not immediately stop upon unbinding. Of course, that hasn't actually been a problem because my activity refuses to unbind from the service at all. Anytime my application gets to unbindService in either of these activities, unbindService throws IllegalArgumentException every time, without exception (hehe).

In the onStop methods:

@Override
public void onStop()
{
  super.onStop();

  if (mBound) {
    try {
      unbindService(mConnection);
    } catch (java.lang.IllegalArgumentException e)
    {
      //Print to log or make toast that it failed
    }
  }
  mBound = false;
}

What I'm wondering is this:

  • Should I be calling unbindService() in the onStop() method? Or at all?
  • Am I calling it correctly?
  • Is there anything peculiar about the way I'm starting/binding the service that I should know about?
  • Am I doing something utterly, completely wrong? I'm new to android programming so that's certainly not out of the question.

Thanks in advance.

EDIT: Here are the ServiceConnection overrides

public void onServiceConnected(ComponentName className, IBinder service) {
  mBound = true;
  mService = IPureService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName arg0) {
  mBound = false;
}

There's some additional code in the player activity, but it's unrelated to the binding itself.

Answer

hackbod picture hackbod · Jul 11, 2011

First, unless you actually need to make calls to this service across processes (that is, from other .apks, or you are using android:process to split up your own .apk into multiple processes for some reason), then I really recommend just dropping the use of aidl. It is more complexity for no gain. The "Local Service Sample" in the Service documentation shows how to do this: http://developer.android.com/reference/android/app/Service.html

Second, doing a bind at the same time as a start is a strong indication of some basic flaw in the design. Starting a service and binding to a service are semantically very different, so will be done at different places based on those different semantics. That is, if both are even done at all... in fact it is an unusual situation where you are using both start and bind with the same service.

In the class implementation of a service for doing music playback, it would use start when it is actively performing playback (so its process doesn't get killed by the system when the user is no longer actively interacting with the application's UI). Starting the service when the user has enters the UI is likely to cause pain because now the start/stopped state of the service is not clearly defined -- it could be started either because it is doing playback or because the user happens to have gone into the app's UI, and now when is the right time to stop it? This is going to be troublesome.

Now as far as when to unbind -- you just need to make sure you always match an unbindService() with a previous bindService(). From your snippets of code it looks like you are doing this, but there are strange things in it like mBound never being set. In fact if you are consistently binding in onStart() and unbinding in onStop(), you should never need to have an mBound to decide whether to unbind, because onStop() is always called after onStart().

So with the code you give here, it doesn't look like there is a problem. If you are getting exceptions, though, there clearly is so it may be elsewhere in your app. To help narrow the problem down, you can use this flag when you call bindService() to get additional information in the log when the failure happens: http://developer.android.com/reference/android/content/Context.html#BIND_DEBUG_UNBIND