48

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.

fersarr
  • 3,399
  • 3
  • 28
  • 35
2ndlife
  • 1,378
  • 3
  • 14
  • 20
  • check http://stackoverflow.com/questions/1916253/bind-service-to-activity-in-android – Sunil Kumar Sahoo Dec 23 '11 at 09:43
  • I did go through this before but that doesnt solve my problem, let's just say that I call UnBindService without calling BindService(), how do u handle the error without the application FC? – 2ndlife Dec 23 '11 at 12:28
  • Couldn't you just catch the exception and move on? – dragonroot Jan 15 '12 at 21:05
  • 1
    @Yury's answer is the right answer. – VansFannel Aug 06 '12 at 16:41
  • It is because you are binding service with getApplicationContext() and then unbinding it with current activity context. It's like giving coin to X and asking for it from Y. It will throw you IllegarlArgumentException :-) I hope this is the answer to question, use same context while binding and unbinding :-) – AZ_ Jan 23 '14 at 13:07

8 Answers8

71

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(..)

AZ_
  • 21,688
  • 25
  • 143
  • 191
Andrey Novikov
  • 5,563
  • 5
  • 30
  • 51
  • 1
    I've followed your answer and I'm still getting the same error. @Yury answer fix the problem. – VansFannel Aug 06 '12 at 16:40
  • both answers are correct : the one from Andrey Novikov and @Yury – Snicolas Aug 08 '12 at 12:42
  • It seems to me this solution would leak the binded service, because I just figured out unbinding must be called on the same context. So testing on a different context might create odd results having actually bound twice to a particular service (if at all possible?) – 3c71 Jan 22 '14 at 04:27
  • I understood why reading this: http://stackoverflow.com/questions/10641144/difference-between-getcontext-getapplicationcontext-getbasecontext-and – fersarr Mar 07 '14 at 07:24
  • 3
    My issue seemed to have stemmed from using the activity context instead of the application context. To prevent botching the context you are using, as noted make sure you are using the application context for all binding/unbinding. – ian.shaun.thomas Jan 27 '16 at 15:13
  • The return value may not be trustable, see http://stackoverflow.com/questions/26127129/doubts-about-bindservice – Kevin Lee Mar 15 '16 at 17:56
  • Use SAME CONTEXT for bindService and unbindService is what solved the problem for me. – Patrick Boos Mar 22 '16 at 16:40
  • ian.shaun.thomas, you were right. The Activity context is no good for binding/unbinding services. Switched to using the application context and the error went away. – Peter Griffin Nov 13 '18 at 11:10
  • Note: isBound is not trustable, bindService() is an asynchronous call. bindService > onServiceConnected > this is where you know the service was properly bound. – Alex Mar 28 '21 at 15:16
  • According to the official android documentation, unbindService should be called even if bindService returns false, so this answer is not correct at all. – Iscle Mar 25 '22 at 08:01
8

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.

VansFannel
  • 45,055
  • 107
  • 359
  • 626
Yury
  • 20,618
  • 7
  • 58
  • 86
  • I'm afraid the given example is for local services, not remote process services. – Surya Wijaya Madjid Oct 16 '12 at 08:28
  • 5
    Unlike the local one, binding to remote services will give us `onServiceDisconnected()` callback if the target service's process dies and `onServiceConnected()` will be delivered again when it's restarted. If you rely on those callbacks for the `mBound` flag, you will get a service connection leak if the attempt to unbind is done in between `onServiceDisconnected()` and `onServiceConnected()` when the `mBound` would be `false`. – Surya Wijaya Madjid Oct 16 '12 at 08:29
  • To solve this issue, don't set mBound to false under onServiceDisconnected. And if there's a need to track connected and disconnected statuses, perhaps introduce another variable mConnected. – Kevin Lee Apr 08 '16 at 19:24
  • 2
    404. Answers posting links instead of excerpts from it should really be encourage – mr5 Jun 22 '16 at 08:08
5

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

getApplicationContext().unbindService(serviceConnection);

With:

unbindService(serviceConnection);
DisplayName
  • 3,093
  • 5
  • 35
  • 42
5

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;
        }
    }

}
Blundell
  • 75,855
  • 30
  • 208
  • 233
  • 2
    This works, but it seems really awkward that you have to maintain 'bound' manually - it seems like it should be an accessible internal variable. – David Doria Oct 01 '13 at 15:40
  • @DavidDoria It might seems awkward but it is really how Android show how to do it : http://developer.android.com/guide/components/bound-services.html – ForceMagic Mar 27 '14 at 13:15
  • This answer worked for me because I realized that I was, stupidly, calling myService.unbindService(myServiceConnectionManager) instead of calling context.unbindService(...) – Chris Sprague Feb 20 '15 at 23:40
  • Might be nice to also handle the other callback: public void onBindingDied( ComponentName name ) – Adam Apr 11 '18 at 17:44
3

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

}
Rohit Singh
  • 16,950
  • 7
  • 90
  • 88
0

I think that guide is not completely correct as mentioned here Surya Wijaya Madjid. Memory leaks can occur when bound service is destryed and not re-connected yet.

I think that this approach is needed:

Service mService;

private final ServiceConnection mServiceConnection = new ServiceConnection()
{
    boolean bound = false;

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        mService = null;
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        mService = ((MyService.ServiceBinder) service).getService();

        if (!bound)
        {
            // do some action - service is bound for the first time
            bound = true;
        }
    }
};

@Override
public void onDestroy()
{
    if (mService != null)
    {
        // do some finalization with mService
    }

    if (mServiceConnection.bound)
    {
        mServiceConnection.bound = false;
        unbindService(mServiceConnection);
    }
    super.onDestroy();
}

public void someMethod()
{
    if (mService != null)
    {
        // to test whether Service is available, I have to test mService, not     mServiceConnection.bound
    }
}
Community
  • 1
  • 1
Bhiefer
  • 1,613
  • 4
  • 16
  • 31
0

Use a variable to record if you have ever bind to a service, and unbind it if the variable is true.

See android official example :

http://androidxref.com/9.0.0_r3/xref/development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java#376

jiaqi jiang
  • 451
  • 3
  • 2
-1

Not sure about all the above answers, it seemed far too complicated while none of these would fit the issue I had.

Only binding/unbinding once at a time, and the service was definitely bound at the time of the unbind() call. Don't want to leak anything, so I just made sure I was using the same context for the bind() and unbind() calls and that solved it permanently! Doing something like this:

any_context.getApplicationContext().bind(...);

...

another_context.getApplicationContext().unbind(...);
3c71
  • 4,313
  • 31
  • 43