【问题标题】:Android: How to safely unbind a serviceAndroid:如何安全地解除绑定服务
【发布时间】:2011-12-23 09:34:24
【问题描述】:

我有一个绑定到应用程序上下文的服务,如下所示:

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

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

由于某种原因,只有有时应用程序上下文没有正确绑定(我无法修复该部分),但是在 onDestroy() 中我执行unbindservice 会引发错误

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

我的问题是:有没有办法安全地调用unbindservice 或者在解除绑定之前检查它是否已经绑定到服务?

提前致谢。

【问题讨论】:

  • 我之前确实经历过这个,但这并没有解决我的问题,假设我调用UnBindService而不调用BindService(),你如何在没有应用程序FC的情况下处理错误?
  • 你不能抓住异常继续前进吗?
  • @Yury 的答案是正确的答案。
  • 这是因为您将服务与 getApplicationContext() 绑定,然后将其与当前活动上下文解除绑定。这就像给 X 一个硬币然后从 Y 那里要它。它会抛出 IllegarlArgumentException :-) 我希望这是问题的答案,在绑定和解除绑定时使用相同的上下文 :-)

标签: android


【解决方案1】:

试试这个:

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

注意:

您应该使用相同的context 来绑定服务和取消绑定服务。如果您将服务与getApplicationContext() 绑定,那么您还应该使用getApplicationContext.unbindService(..)

【讨论】:

  • 我已按照您的回答进行操作,但仍然遇到同样的错误。 @Yury 回答解决问题。
  • 两个答案都是正确的:Andrey Novikov 和@Yury 的答案
  • 在我看来,这个解决方案会泄漏绑定的服务,因为我刚刚发现必须在相同的上下文中调用取消绑定。因此,在不同的上下文中进行测试可能会产生奇怪的结果,实际上两次绑定到特定服务(如果可能的话?)
  • 我明白为什么要读这个:stackoverflow.com/questions/10641144/…
  • 我的问题似乎源于使用活动上下文而不是应用程序上下文。为防止破坏您正在使用的上下文,如上所述,请确保您使用应用程序上下文进行所有绑定/取消绑定。
【解决方案2】:

Here 你可以找到一个很好的解释和源代码如何使用绑定服务。在您的情况下,您应该覆盖 ServiceConnection 对象的方法(onServiceConnectedonServiceDisconnected)。然后你可以在你的代码中检查mBound 变量。

【讨论】:

  • 恐怕给定的示例是针对本地服务的,而不是远程进程服务。
  • 与本地不同,如果目标服务的进程死亡,绑定到远程服务会给我们onServiceDisconnected()回调,并且onServiceConnected()将在它重新启动时再次传递。如果您依赖mBound 标志的这些回调,如果在onServiceDisconnected()onServiceConnected() 之间完成解除绑定的尝试,而mBound 将是false,您将获得服务连接泄漏。
  • 为了解决这个问题,在onServiceDisconnected下不要将mBound设置为false。如果需要跟踪连接和断开状态,或许可以引入另一个变量 mConnected。
  • 404。应该鼓励发布链接而不是摘录的答案
【解决方案3】:

完全按照安德烈·诺维科夫的建议去做对我来说并不奏效。 我简单地替换了:

getApplicationContext().unbindService(serviceConnection);

与:

unbindService(serviceConnection);

【讨论】:

  • 什么是serviceConnection
【解决方案4】:

我发现有两个问题。尝试多次绑定并多次解除绑定。

解决方案:

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

}

【讨论】:

  • 这行得通,但您必须手动维护“绑定”似乎真的很尴尬——它似乎应该是一个可访问的内部变量。
  • @DavidDoria 这可能看起来很尴尬,但它确实是 Android 展示如何做到这一点的方式:developer.android.com/guide/components/bound-services.html
  • 这个答案对我有用,因为我意识到我愚蠢地调用了 myService.unbindService(myServiceConnectionManager) 而不是调用 context.unbindService(...)
  • 处理另一个回调可能会很好:public void onBindingDied(ComponentName name)
【解决方案5】:

为什么会出现此错误?

当您尝试注销未注册的服务时。

有哪些常见的例子?

  • 使用不同的上下文绑定和取消绑定服务。
  • 拨打unBind(mserviceConnection)超过bind(...)

第一点是不言自明的。让我们更深入地探讨第二个错误来源。调试您的 bind() 和 unbind() 调用。如果您按这些顺序看到调用,那么您的应用程序最终将获得IllegalArgumentException

我们怎样才能避免这种情况?
在 Activity 中绑定和取消绑定服务应该考虑两种方式。 Android 文档建议

  • 如果您只想在 Activity 可见时与服务交互,那么

onStart() 中的 bindService() 和 onStop() 中的 unbindService()

Your Activity {

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

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

} 
  • 如果您想与服务交互,即使 Activity 处于后台,那么

onCreate() 中的 bindService() 和 onDestroy() 中的 unbindService()

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

}

【讨论】:

    【解决方案6】:

    我认为guide 并不完全正确,正如here Surya Wijaya Madjid 所述。当绑定服务被销毁且尚未重新连接时,可能会发生内存泄漏。

    我认为需要这种方法:

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

    【讨论】:

      【解决方案7】:

      使用变量记录是否曾经绑定到服务,如果变量为真,则取消绑定。

      查看android官方示例:

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

      【讨论】:

        【解决方案8】:

        不确定以上所有答案,看起来太复杂了,而这些都不适合我遇到的问题。

        一次只绑定/取消绑定一次,并且在 unbind() 调用时服务肯定是绑定的。不想泄漏任何东西,所以我只是确保我对 bind() 和 unbind() 调用使用相同的上下文,并且永久解决了它!做这样的事情:

        any_context.getApplicationContext().bind(...);
        
        ...
        
        another_context.getApplicationContext().unbind(...);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多