Android:绑定服务被销毁时如何避免服务连接泄漏

Android: How to avoid service connection leak when bound service is destroyed

我想就此问题寻求帮助。这看起来很常见,但我没有在任何地方找到答案。只提到here

我有一个服务 A 使用服务连接 C 绑定到第二个服务 B。

  1. A 正在运行并绑定到 B
  2. B 停止并销毁(无错误状态 - 它正常完成)
  3. onServiceDisconnected() 在 C 上调用
    • 我发现一些关于这个的提及,它不是正常调用的。但是当B被销毁时我可以观察到它。
  4. 现在用C应该怎么办?
    • 根据guide,我应该什么都不做(因为B被破坏并断开连接)。但是这样会导致A被销毁时出现service leak异常,因为C还处于活动状态!

所以我的问题是:错误在哪里?在我的场景或谷歌的指南中。

我尝试将 A 中的代码更改为以下形式,看起来效果很好,但很丑陋:

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

还有其他正确处理方法吗?非常感谢您的帮助或意见。

您考虑过使用管道吗?

这样就可以实现良好的通信,无需两个服务。

如果你想了解更多关于高效多线程的信息,我推荐阅读这本书:

Efficient multi threading in Android

转到第 4 章第 39 页,这里有完整的管道和相关内容的章节。

所以我使用了问题中的方法并将其包装到 SafeServiceConnection class。我知道通常在所有绑定未绑定之前服务不会被破坏,但这并不总是需要的行为。

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