Android: How to safely unbind a service

I have a service which is binded to application context like this:

getApplicationContext().bindService(
                    new Intent(this, ServiceUI.class),
                    serviceConnection,
                    Context.BIND_AUTO_CREATE
            );

protected void onDestroy() {
            super.onDestroy();                  
            getApplicationContext().unbindService(serviceConnection);
        }

For some reason, only sometimes the application context does not bind properly (I can't fix that part), however in onDestroy() I do unbindservice which throws an error

java.lang.IllegalArgumentException: Service not registered: tools.cdevice.Devices$mainServiceConnection.

My question is: Is there a way to call unbindservice safely or check if it is already bound to a service before unbinding it?

Thanks in advance.


Solution 1:

Try this:

boolean isBound = false;
...
isBound = getApplicationContext().bindService( new Intent(getApplicationContext(), ServiceUI.class), serviceConnection, Context.BIND_AUTO_CREATE );
...
if (isBound)
    getApplicationContext().unbindService(serviceConnection);

Note:

You should use same context for binding a service and unbinding a service. If you are binding Service with getApplicationContext() so you should also use getApplicationContext.unbindService(..)

Solution 2:

Here you can find a nice explanation and source codes how to work with bound services. In your case you should override methods (onServiceConnected and onServiceDisconnected) of ServiceConnection object. Then you can just check mBound variable in your code.

Solution 3:

Doing exactly what Andrey Novikov proposed didn't work for me. I simply replaced:

getApplicationContext().unbindService(serviceConnection);

With:

unbindService(serviceConnection);

Solution 4:

I found there are two issues. Attempting to bind more than once and also attempting to unbind more than once.

Solution:

public class ServiceConnectionManager implements ServiceConnection {

    private final Context context;
    private final Class<? extends Service> service;
    private boolean attemptingToBind = false;
    private boolean bound = false;

    public ServiceConnectionManager(Context context, Class<? extends Service> service) {
        this.context = context;
        this.service = service;
    }

    public void bindToService() {
        if (!attemptingToBind) {
            attemptingToBind = true;
            context.bindService(new Intent(context, service), this, Context.BIND_AUTO_CREATE);
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        attemptingToBind = false;
        bound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        bound = false;
    }

    public void unbindFromService() {
        attemptingToBind = false;
        if (bound) {
            context.unbindService(this);
            bound = false;
        }
    }

}

Solution 5:

Why do we get this error?

When you try to unregister a service which is not registered.

What are some common examples?

  • Binding and Unbinding a service with different Context.
  • calling unBind(mserviceConnection) more than bind(...)

First point is self explanatory. Lets explore the second source of error more deeply. Debug your bind() and unbind() calls. If you see calls in these order then your application will end up getting the IllegalArgumentException.

enter image description here

How can we avoid this?
There are two ways you should consider to bind and unbind a service in Activity. Android docs recommend that

  • If you want to interact with a service only when the Activity is visible then

bindService() in onStart() and unbindService() in onStop()

Your Activity {

    @Override
    public void onStart(){
       super.onStart();
       bindService(intent, mConnection , Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onStop(){
       super.onStop();
       unbindService(mConnection);
    }

} 
  • If you want to interact with a service even an Activity is in Background then

bindService() in onCreate() and unbindService() in onDestroy()

Your Activity {

    @Override
    public void onCreate(Bindle sis){
       super.onCreate(sis);
        ....
        bindService(intent, mConnection , Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }         

}