【问题标题】:Android: service destroyed when display is rotatedAndroid:旋转显示时服务被破坏
【发布时间】:2011-04-03 06:08:06
【问题描述】:

我有一个在单独进程中运行的服务。我发现在主进程 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: DEBUG/MyApp(841): main onDestroy
04-03 05:15:12.895: 调试/MyApp(847): 服务 onUnbind
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: DEBUG/MyApp(847): service onBind

所以我的问题是:

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

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

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

【问题讨论】:

    标签: android service oncreate ondestroy


    【解决方案1】:

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

    2) 检查START_STICKY 标志,如here 所述。无论调用Context

    一般来说,onDestroy() 表示您的活动即将被终止。当您旋转显示器时,Activity 将被杀死并重新创建。您负责以适当的方法将任何状态保存到Bundle,然后将其恢复到onCreate()

    【讨论】:

    • 我已经从我的服务的 onStartCommand() 返回了 START_STICKY。我应该在开篇文章中提到这一点。
    • 在绑定之前用startService(intent)启动服务应该可以防止activity被销毁时服务重启
    【解决方案2】:

    你的服务是否被杀死:

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

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

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

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

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

    【讨论】:

    • 1.我不确定你是什么意思。在主 UI 线程的 onDestroy() 中,第二个活动如何在堆栈上? 2. 配置更改是什么意思? --- 为什么我不能在这个文本框中按回车? ---
    • 很抱歉缺少段落。我不能在这个文本框中使用回车?添加到我之前写的内容......我不在乎我的服务是否退出。但是我不希望服务退出然后立即重新启动,这就是旋转显示器时发生的情况。
    【解决方案3】:

    只有当以下两个都为真时,服务才会被销毁:

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

    这是可行的,因为它需要在销毁服务之前取消绑定活动和停止自我()服务,这意味着在服务被销毁之前存在超时。

    【讨论】:

      猜你喜欢
      • 2015-01-29
      • 1970-01-01
      • 1970-01-01
      • 2013-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-13
      • 1970-01-01
      相关资源
      最近更新 更多