2

I would like to ask for a help with this issue. It looks like quite common scenario but I didn't found an answer anywhere. It is only mentioned here

I have one service A that is bound to second service B using service connection C.

  1. A is running and bound to B
  2. B is stopped and destroyed (no error state - it normally finishes)
  3. onServiceDisconnected() is called on C
    • I have found some mentions about this that it is not called normally. But I can observe it when B is destroyed.
  4. What should I do with C now?
    • According guide, I should do nothing (because B is destroyed and disconnected). But this leads to service leak exception when A is destroyed because C is still active!

So my question is: Where is the bug? In my scenario or in Google's guide.

I tried to change the code in A to following form and it looks that it works well, but it is ugly:

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)
    {
        // I have to test mService, not mServiceConnection.bound
    }
}

Is there any other option how to handle it correctly? Thank You very much for help or opinion.

Community
  • 1
  • 1
Bhiefer
  • 1,613
  • 4
  • 16
  • 31
  • Why do you need the two `Services` to be bound to one another? I have never seen this pattern. – Emmanuel Jan 03 '15 at 23:33
  • ugly? what's ugly with unbindService()? – pskink Jan 04 '15 at 00:06
  • Emanuel: There is a synchronization service and service working with storage. I need there immediate oneway interaction to avoid conflicts. But I think that it doesn't matter whether A is service or something else. It can be Activity A. – Bhiefer Jan 04 '15 at 00:23
  • What is ugly? I don't like that each connection has to have two state variables (bound and mService) to handle such simple thing. It is at least quite clumsy. Android guide uses simpler way that causes memory leaks. unbindService() itself is not ugly at all :) – Bhiefer Jan 04 '15 at 00:32

2 Answers2

0

Did you consider using pipes?

This way you can achieve nice communication, without two services.

If you want more information about efficient multithreading, I recommend to read this book:

Efficient multi threading in Android

Go to chapter 4, page 39, here you have whole chapter for pipes and connected stuff.

Lii
  • 11,553
  • 8
  • 64
  • 88
kamilmasta
  • 129
  • 7
  • Thank you for link. Quite nice publication. Unfortunately this is not what I need. Two services was only an example. In our app, we are using ServiceConnection many times in Activities too. I don't want to use something else than ServiceConnection. I just want know some correct and nice way how to handle it... – Bhiefer Jan 04 '15 at 10:36
0

So I used the approach from my question and wrapped it to SafeServiceConnection class. I know that generally service is not destroyed before all bindings are unbound but it is not always wanted behaviour.

public class SafeServiceConnection<T extends Service> implements ServiceConnection
{
    private final Logger log = new Logger(SafeServiceConnection.class);

    boolean mBound = false;
    T mService;

    @SuppressWarnings("unchecked")
    @Override
    final public void onServiceConnected(ComponentName name, IBinder service)
    {
        mService = ((ServiceBinder<T>) service).getService();

        if (mService == null)
        {
            return;
        }

        if (!mBound)
        {
            mBound = true;

            onServiceConnected(name, mService, true);
        }
        else
        {
            onServiceConnected(name, mService, false);
        }
    }

    public void onServiceConnected(ComponentName name, T service, boolean firstConnection)
    {

    }

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

    public void bind(Context context, Intent intent, int flags)
    {
        context.bindService(intent, this, flags);
    }

    public void bind(Context context, Intent intent)
    {
        bind(context, intent, Context.BIND_AUTO_CREATE);
    }

    public void startAndBind(Context context, Intent intent)
    {
        context.startService(intent);
        bind(context, intent);
    }

    public void unbind(Context context)
    {
        if (mBound)
        {
            if (context != null)
            {
                context.unbindService(this);
            }
            else
            {
                log.e("Context is null");
            }

            mBound = false;
        }
    }

    public T getService()
    {
        return mService;
    }

    public boolean isConnected()
    {
        return mService != null;
    }
}
Bhiefer
  • 1,613
  • 4
  • 16
  • 31