干净地绑定/取消绑定到应用程序中的服务

发布于 2025-01-07 21:53:31 字数 733 浏览 1 评论 0原文

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

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

我已经从 Application 扩展并绑定到 Application#onCreate()。但是我现在遇到的问题是,我不知道我的应用程序何时存在,因为 Application #onTerminate() 永远不会被调用,请参阅 JavaDoc:

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

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

I have an Android application that is binding to a persistent service (once started with startService()).

The service is an integral part of the application and thus is used in almost every Activity. Hence I want to bind to the service just once (instead of binding/unbinding in every Activity) and keep the binding during the lifetime of my application.

I've extended from Application and bind to the service in Application#onCreate(). However I now have the problem that I don't know when my application exists since Application#onTerminate() is never called, see JavaDoc:

This method is for use in emulated process environments. It will never
be called on a production Android device, where processes are removed
by simply killing them; no user code (including this callback) is
executed when doing so.

So how do I cleanly unbind from a service bound in Application?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

金橙橙 2025-01-14 21:53:31

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

这是一个例子:

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

I solved this problem by counting the references to the service binding in the Application. Every Activity has to call acquireBinding() in their onCreate() methods and call releaseBinding() in onDestroy(). If the reference counter reaches zero the binding is released.

Here's an example:

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();
    }
}
花想c 2025-01-14 21:53:31

来自斯文的回答:

我通过计算对服务的引用解决了这个问题
在应用程序中绑定。每个 Activity 都必须调用
在其 onCreate() 方法中 acquireBinding() 并调用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 版本),以确保您的引用计数不会混乱。

谁知道发布你的应用程序的服务会如此复杂!

From Sven's answer:

I solved this problem by counting the references to the service
binding in the Application. Every Activity has to call
acquireBinding() in their onCreate() methods and call releaseBinding()
in onDestroy(). If the reference counter reaches zero the binding is
released.

I agree, BUT you shouldn't do it in onDestroy - that will often not get called.

Instead I suggest the following (based on your code sample)...

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

BUT, my use of isFinishing() is just a thought - I'm not certain that it is reliable. Perhaps onPause/onStop get called with isFinishing() false, but then the activity gets killed - and your releaseBinding() never gets called.

If you get rid of the isFinishing check I think you need to move the acquireBinding() call from onCreate to onStart/onResume (depending on sdk version), to ensure that your ref count doesn't get messed up.

Who knew that releasing your app's service would be so complicated!

朕就是辣么酷 2025-01-14 21:53:31

这种情况下有必要解除绑定吗?无论如何,应用程序都会被杀死。我尝试实现一个示例应用程序,在不解除绑定的情况下执行此操作,并且它似乎工作正常。

Is unbinding necessary at all in this case? The application gets killed anyway. I tried implementing a sample application doing this without unbinding and it seems to work properly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文