【问题标题】:Cleanly binding/unbinding to a Service in an Application干净地绑定/取消绑定到应用程序中的服务
【发布时间】:2012-02-20 16:03:45
【问题描述】:

我有一个绑定到持久服务的 Android 应用程序(曾经以 startService() 启动)。

服务是应用程序的一个组成部分,因此几乎用于每个活动。因此,我只想绑定到服务一次(而不是在每个 Activity 中绑定/取消绑定)并在我的应用程序的生命周期内保持绑定。

我已从Application 扩展并绑定到Application#onCreate() 中的服务。但是我现在有一个问题,我不知道我的应用程序何时存在,因为从未调用过 Application#onTerminate(),请参阅 JavaDoc:

此方法用于模拟过程环境。它永远不会 在生产 Android 设备上调用,其中删除了进程 通过简单地杀死他们;没有用户代码(包括此回调) 这样做时执行。

那么我如何彻底解除绑定在应用程序中的服务?

【问题讨论】:

    标签: android service bind unbind


    【解决方案1】:

    我通过计算Application 中对服务绑定的引用解决了这个问题。每个Activity 都必须在他们的onCreate() 方法中调用acquireBinding(),并在onDestroy() 中调用releaseBinding()。如果引用计数器达到零,则释放绑定。

    这是一个例子:

    class MyApp extends Application {
        private final AtomicInteger refCount = new AtomicInteger();
        private Binding binding;
    
        @Override
        public void onCreate() {
            // create service binding here
        }
    
        public Binding acquireBinding() {
            refCount.incrementAndGet();
            return binding;
        }
    
        public void releaseBinding() {
            if (refCount.get() == 0 || refCount.decrementAndGet() == 0) {
                // release binding
            }
        }
    }
    
    // Base Activity for all other Activities
    abstract class MyBaseActivity extend Activity {
        protected MyApp app;
        protected Binding binding;
    
        @Override
        public void onCreate(Bundle savedBundleState) {
            super.onCreate(savedBundleState);
            this.app = (MyApp) getApplication();
            this.binding = this.app.acquireBinding();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            this.app.releaseBinding();
        }
    }
    

    【讨论】:

    • 您的服务如何在屏幕旋转中幸存下来?
    • 什么意思? Android 中的 Service 没有 UI,因此它会一直运行。在这种情况下,该服务之前以 context.startService() 启动并被标记为粘性,因此它会一直运行,直到它被手动停止或自行停止。
    • 我明白了。我在想释放绑定会停止服务,但在这种混合服务情况下不会。释放绑定是为了避免泄露服务连接。
    • 正确。该服务以startService() 启动,但也绑定到。
    • 非常感谢您的回答。像魅力一样工作。
    【解决方案2】:

    来自斯文的回答:

    我通过计算对服务的引用解决了这个问题 在应用程序中绑定。每个活动都必须调用 在他们的 onCreate() 方法中获取绑定 () 并调用 releaseBinding() 在 onDestroy() 中。如果引用计数器达到零,则绑定为 发布。

    我同意,但你不应该在 onDestroy 中这样做 - 这通常不会被调用。

    相反,我建议以下(基于您的代码示例)...

    // Base Activity for all other Activities
    abstract class MyBaseActivity extend Activity {
        protected MyApp app;
        protected Binding binding;
    
        @Override
        public void onCreate(Bundle savedBundleState) {
            super.onCreate(savedBundleState);
            this.app = (MyApp) getApplication();
            this.binding = this.app.acquireBinding();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            // Pre-HC, activity is killable after this.
            if ((11 > Build.VERSION.SDK_INT) && (isFinishing()))
                onFinishing();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            if ((10 < Build.VERSION.SDK_INT) && (isFinishing()))
                onFinishing();
        }
    
        protected void onFinishing() {
            // Do all activity clean-up here.
            this.app.releaseBinding();          
        }
    }
    

    但是,我对 isFinishing() 的使用只是一个想法——我不确定它是否可靠。或许 onPause/onStop 会在 isFinishing() 为 false 的情况下被调用,但随后 Activity 会被终止 - 而且您的 releaseBinding() 永远不会被调用。

    如果您摆脱 isFinishing 检查,我认为您需要将 acquireBinding() 调用从 onCreate 移动到 onStart/onResume(取决于 sdk 版本),以确保您的引用计数不会被弄乱。

    谁知道发布你的应用服务会这么复杂!

    【讨论】:

    • 感谢您的澄清。我会看看这个。我也觉得我的解决方案并不总是可靠的。
    【解决方案3】:

    在这种情况下是否需要解除绑定?该应用程序无论如何都会被杀死。我尝试在不取消绑定的情况下实现一个示例应用程序,它似乎可以正常工作。

    【讨论】:

    • 如果您不解绑,您应该会在日志中看到“服务连接泄漏”错误。
    猜你喜欢
    • 1970-01-01
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    相关资源
    最近更新 更多