Android:当显示屏旋转时服务被销毁

5
我有一个在单独进程中运行的服务。我发现即使我已经提供了应用程序上下文与绑定并指定了BIND_AUTO_CREATE,在主进程UI线程从onDestroy()退出后,我的服务也被销毁了。
在我的主进程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: 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
所以我的问题是:
1)我对绑定/解绑的理解正确吗?
2)有没有办法让我的服务在UI线程的onDestroy()被调用时不被销毁?
问题#2的hack方法是永远不解绑。但我不喜欢它,因为每次调用onDestroy()时都会泄漏一个绑定。我可以“记住”我有一个泄漏的绑定,并且仅泄漏那一个,但这样就会有级联的hack,而且很难看。
3个回答

4

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

2) 可以查看这里描述的START_STICKY标志。这应该允许您启动服务并使其在调用Context发生任何事情时保持运行。

总的来说,onDestroy()意味着您的活动即将被杀死。当您旋转显示器时,Activity会被杀死并重新创建。您需要在适当的方法中将任何状态保存到Bundle中,然后在onCreate()中恢复它。


我已经从我的服务的onStartCommand()返回了START_STICKY。我应该在开头的帖子中提到这一点。 - Syrinx
在绑定服务之前使用 startService(intent) 启动服务,可以防止活动销毁时服务重新启动。 - Nicolas Girardin

0

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

  1. 所有的bindService()调用都有相应的unbindService()调用匹配。
  2. 如果某人调用了startService(),该服务也必须被调用stopService()或服务自己调用stopSelf()。

一个服务可以同时启动和连接绑定。在这种情况下,只要服务已启动或者有一个或多个标记为Context.BIND_AUTO_CREATE的连接存在,系统就会保持服务运行状态。一旦这些情况都不再成立,服务的onDestroy()方法将被调用,服务将被有效地终止。

这提供了一种正确而安全的解决方案!

假设有2个需要服务的活动(本例中为'viewer'和'chat'),如果两者都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()),这意味着在服务被销毁之前有一个超时时间。


0

你的服务会被杀死吗:

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

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

我认为一般的经验法则是,你不能确定你的活动和服务何时会被杀死。如果这阻碍了你想要实现的某些事情,可能有一个巧妙的方法可以解决它。

编辑 - 你实际上可以处理方向配置更改,这样你的活动就不会重新启动。有关更多信息,请参见this answer的后半部分。

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

  1. 我不确定你的意思。当主UI线程的onDestroy()被调用时,第二个活动怎么可能在栈中呢?
  2. 你所说的配置更改是什么意思?为什么我不能在这个文本框中按回车键?
- Syrinx
抱歉没有分段。我不能在这个文本框中使用回车键?补充我之前写的...我不在乎我的服务是否退出。但是我不希望服务退出后立即重新启动,这就是当显示器旋转时发生的事情。 - Syrinx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接