Android:旋转显示器时服务被破坏

发布于 2024-10-28 18:55:53 字数 1580 浏览 5 评论 0原文

我有一个在单独进程中运行的服务。我发现主进程 UI 线程从 onDestroy() 退出后,即使我已为应用程序上下文提供了绑定并指定了 BIND_AUTO_CREATE,我的服务仍被销毁。

在我的主进程的 UI 线程 onCreate() 中,我得到了这个绑定代码:

Intent intent = new Intent(mAppContext, MyService.class);
mAppContext.bindService(intent, mMyServiceConnection, Context.BIND_AUTO_CREATE);

在我的主进程的 UI 线程 onDestroy() 中,我得到了这个解除绑定代码:

mAppContext.unbindService(mMyServiceConnection);

请注意,我从不调用 stopService()。

Android 的bindService() 文档说:

只要调用上下文存在,系统就会认为该服务需要。

如果我没看错的话,因为我提供了应用程序的上下文,所以该服务被认为是系统在应用程序的生命周期中所需要的。

我认为应用程序的上下文可能会因 onDestroy() 而终止。 Android 文档中对 getApplicationContext() 的描述如下:

返回当前进程的单个全局应用程序对象的上下文。

如果应用程序的上下文因 onDestroy() 而终止,那么我认为 Android 有一个大问题。问题是,当显示器旋转时,会调用 onDestroy()(并立即调用 onCreate())。因此,效果是当显示器旋转时——在我的例子中,这种情况经常发生! -- 我的服务总是退出。

请注意,我的应用程序进程的 pid 永远不会改变,即它是相同的进程。鉴于 getApplicationContext() 的文档指出“当前进程”,这一点很重要。

以下是我的调试日志显示的内容:

04-03 05:15:12.874: 调试/MyApp(841): main onDestroy
04-03 05:15:12.895: 调试/MyApp(847): 解除绑定服务
04-03 05:15:12.895: 调试/MyApp(847): 服务 onDestroy
04-03 05:15:12.934: 调试/MyApp(841): 主要 onCreate
04-03 05:15:12.966:调试/MyApp(847):服务onCreate
04-03 05:15:12.975:调试/MyApp(847):服务onBind

所以我的问题是:

1)我对绑定/解除绑定的理解是否正确?

2)有没有办法让我的服务在调用UI线程的onDestroy()时不被销毁?

问题#2 的一个技巧是永远不要解除绑定。但我不喜欢它,因为每次调用 onDestroy() 时我都会泄漏绑定。我可以“记得”我有一个泄露的绑定,并且只泄露了那个,但随后我遇到了级联黑​​客攻击,这真的很丑陋。

I've got a service that is running in a separate process. I'm finding that after the main process UI thread exits from onDestroy() that my service is being destroyed even though I've provided the application context with the binding and specified BIND_AUTO_CREATE.

In my main process' UI thread onCreate() I've got this binding code:

Intent intent = new Intent(mAppContext, MyService.class);
mAppContext.bindService(intent, mMyServiceConnection, Context.BIND_AUTO_CREATE);

In my main process' UI thread onDestroy() I've got this unbinding code:

mAppContext.unbindService(mMyServiceConnection);

Note that I never call stopService().

Android's documentation for bindService() says:

The service will be considered required by the system only for as long as the calling context exists.

If I'm reading that correctly, because I supplied the application's context, the service is considered required by the system for the life of the application.

I have thought that maybe the application's context dies with onDestroy(). This is what Android's documentation says for getApplicationContext():

Return the context of the single, global Application object of the current process.

If the application's context dies with onDestroy(), then I think Android has a big issue. The issue is that when the display is rotated, onDestroy() is called (and immediately followed by onCreate()). Thus the effect is that when the display is rotated -- and it occurs quite frequently in my case! -- my service always exits.

Note that the pid of my app's process never changes, i.e. it is the same process. That is important in light of the documentation for getApplicationContext() stating "current process."

Here are what my debug logs show:

04-03 05:15:12.874: DEBUG/MyApp(841): main onDestroy
04-03 05:15:12.895: DEBUG/MyApp(847): service onUnbind
04-03 05:15:12.895: DEBUG/MyApp(847): service onDestroy
04-03 05:15:12.934: DEBUG/MyApp(841): main onCreate
04-03 05:15:12.966: DEBUG/MyApp(847): service onCreate
04-03 05:15:12.975: DEBUG/MyApp(847): service onBind

So my questions are:

1) Is my understanding about binding/unbinding correct?

2) Is there a way to have my service not get destroyed when UI thread's onDestroy() is called?

A hack for question #2 is to never unbind. But I don't like it because then I am leaking a binding every time onDestroy() is called. I could "remember" that I've got one leaked binding, and leak just that one, but then I've got cascaded hacks and it's real ugly.

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

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

发布评论

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

评论(3

阳光下的泡沫是彩色的 2024-11-04 18:55:54

您的服务是否会被终止

  1. 如果堆栈上有第二个 Activity
  2. ?如果你处理配置更改?

为什么在您的应用程序被销毁后您需要您的服务保持活动状态?

我认为一般的经验法则是,您无法确定您的活动和服务何时会被终止。如果这阻碍了您想要实现的目标,那么可能有一个聪明的方法可以解决它。

编辑 - 您实际上可以处理方向配置更改,以便您的 Activity 不会重新启动。有关详细信息,请参阅此答案的后半部分。

关于“第二个”Activity:想象一下,您启动活动 A,然后启动活动 B。现在,您在显示 B 时旋转屏幕,导致 B 重新启动。此时A会重新启动吗?我不确定,但我有一种预感,A 往往会保持活动状态,并在方向更改期间使您的应用程序保持活动状态。如果这就是您的目标,这可能是保持服务活力的另一种策略。

Does your service get killed:

  1. if there is a second Activity on the stack?
  2. if you handle configuration changes?

Why do you need your service to stay alive after your application has been destroyed?

I think the general rule of thumb is that you can't be sure when your activities and services will be killed. If this is blocking something you're trying to achieve, there may be a clever way around it.

Edit - you can actually handle the orientation configuration change so that your activity isn't restarted. For more info see the second half of this answer.

About the "second" Activity: Image you start activity A and then activity B. Now you rotate the screen while B is showing, causing B to restart. Will A be restarted at this point? I'm not sure, but I have a hunch that A will tend to stay alive and keep your application alive during the orientation change. This might be another strategy for keeping your service alive if that's what you're aiming for.

你的心境我的脸 2024-11-04 18:55:54

仅当满足以下两个条件时,服务才会被销毁:

  1. 所有对bindService() 的调用均已与对unbindService() 的相应调用相匹配。
  2. 如果有人调用了 startService(),那么有人也调用了 stopService() 或调用了 stopSelf() 的服务。

服务可以启动并绑定连接。在这种情况下,只要服务已启动或存在一个或多个带有 Context.BIND_AUTO_CREATE 标志的连接,系统就会保持服务运行。一旦这两种情况都不成立,服务的 onDestroy() 方法就会被调用,服务就会被有效终止。

这提供了一个很好的解决方案,而且相当正确且安全!

假设有 2 个活动(本例中为“查看者”和“聊天”)需要服务,如果同时有 bindService 和 startService。他们还使用活页夹在 onStart 和 onStop 期间更新“viewer_connected”和“chat_connected”。

然后,服务在线程中运行一个循环来执行以下操作:

public isRunning = true;
while (isRunning) {

    if (viewer_connected) {
        // send update to viewer activity
    }

    if (chat_connected) {
        // send update to chat activity
    }

    try {
        Thread.sleep(5000);
    } catch (Exception e) { isRunning=false; }

    // 3 second timeout before destroying service
    if (!viewer_connected && !chat_connected) {
        try { Thread.sleep(3000); } catch (Exception e) { isRunning=false; }

        if (!viewer_connected && !chat_connected) isRunning=false;
    }

}
stopSelf();

这是有效的,因为它需要在销毁服务之前取消绑定活动和服务 stopself(),这意味着在服务被销毁之前有一个超时。

a service is destroyed only when both of the following are true:

  1. All calls to bindService() have been matched by corresponding calls to unbindService().
  2. If somebody called startService(), somebody also called stopService() or the service called stopSelf().

A service can be both started and have connections bound to it. In such a case, the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated.

this gives a great solution which is also fairly correct and safe!

say there are 2 activities ('viewer' and 'chat' in this example) that need a service, if both bindService AND startService. Also using the binder they update 'viewer_connected' and 'chat_connected' during onStart and onStop.

Then the service runs a loop in a thread that does this:

public isRunning = true;
while (isRunning) {

    if (viewer_connected) {
        // send update to viewer activity
    }

    if (chat_connected) {
        // send update to chat activity
    }

    try {
        Thread.sleep(5000);
    } catch (Exception e) { isRunning=false; }

    // 3 second timeout before destroying service
    if (!viewer_connected && !chat_connected) {
        try { Thread.sleep(3000); } catch (Exception e) { isRunning=false; }

        if (!viewer_connected && !chat_connected) isRunning=false;
    }

}
stopSelf();

This works because it needs both the activities to unbind AND the service to stopself() before it destroys the service, meaning there is a timeout before the service gets destroyed.

浅黛梨妆こ 2024-11-04 18:55:53

1)是的,我认为你的理解是正确的(我说我认为是因为我认为我理解你在说什么;-))。您使用的标志意味着“如果有人尝试绑定到它,则自动启动此服务,并在有人绑定到它时保持它运行,但一旦没有人绑定到它,请随意杀死它”。

2) 查看 此处所述的 START_STICKY 标志。这应该允许您启动服务并保持其运行,无论调用 Context 发生什么情况。

一般来说,onDestroy() 意味着您的 Activity 即将被终止。当您旋转显示器时,Activity 将被终止并重新创建。您负责使用适当的方法将任何状态保存到 Bundle,然后在 onCreate() 中恢复它。

1) Yes, I think your understanding is correct (I say I think because I think I understand what you're saying ;-) ). The flag you are using means "start this service automatically if somebody tries to bind to it and keep it running as long as somebody is bound to it, but once nobody is bound to it, feel free to kill it".

2) Check out the START_STICKY flag as described here. That should allow you to start the service and keep it running regardless of what happens to the calling Context

In general, onDestroy() means your activity is about to be killed. When you rotate the display, the Activity is killed and recreated. You are responsible for saving any state to the Bundle in the appropriate method and then restoring it in onCreate().

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