38

我正在开发一个应用程序,其中创建了一个后台服务来收集传感器数据。我从我的活动开始服务:

startService(new Intent(this, MyService.class));

我创建了服务,因此如果应用程序被销毁,后台服务仍会继续收集数据。我试过这个,它在一定程度上奏效了。我的问题是,当我终止应用程序时,服务似乎重新启动,因为调用了onCreate()服务和onStart()方法。请问有什么方法可以不重启服务吗?

更新:

正如下面的答案所建议的,我在服务中添加了以下方法,但没有运气。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_NOT_STICKY;
}
4

6 回答 6

20

这取决于 onStartCommand 中返回的值。

您必须返回 START_NOT_STICKY

根据文档

对于已启动的服务,它们可以决定运行另外两种主要的操作模式,具体取决于它们从 onStartCommand() 返回的值:START_STICKY 用于根据需要显式启动和停止的服务,而 START_NOT_STICKY 或 START_REDELIVER_INTENT 用于适用于只应在处理发送给它们的任何命令时保持运行的服务

简而言之:如果您返回 START_STICKY,则只要资源可用,就会重新创建服务。如果您返回 START_NOT_STICKY 您必须重新激活发送新意图的服务。

由于所有这些都引发了我的好奇心,我制作了一个示例应用程序来测试它。您可以在此处找到包含所有来源的 zip 有一个 startService 按钮和一个 stopService 按钮,它们可以满足您对它们的期望。该服务在 onStartCommand 中返回 START_NOT_STICKY。我在 onCreate、onStartCommand 和 onDestroy 中放置了 toast。

这里会发生什么:

  • 如果我按开始,则会调用 onCreate 和 onStart
  • 如果我按下停止,就会触发 onDestroy
  • 如果我按 start 两次,onCreate 被调用一次,onStartCommand 被调用两次

所以它的行为与预期的一样。

如果我按照您的描述启动服务并终止应用程序,则不会调用 onDestroy,但不会调用 onCreate 或 onStart。

如果我回到应用程序并再次按启动,则会调用 onCreate,这意味着,正如我之前所写的,START_NOT_STICKY 会阻止服务自动重启。

我猜您的应用程序中有其他东西可以再次启动服务(可能是待处理的意图)。

于 2013-03-16T18:28:10.137 回答
5

应用程序和服务存在于同一个进程中,这意味着当应用程序被终止时,您的服务也会被终止。更改 onStartCommand 的返回值不会影响此过程。它只是告诉服务在您告诉它时启动/停止,或者在它完成所需的操作时启动/停止。正如您对原始帖子的评论中所述,将其设置为前台进程是可行的,但这实际上只是迫使服务具有高优先级,而不是解决问题。

要更改服务以使其被单独终止并假设它是已启动的服务而不是由于使用 onStartCommand 而绑定的服务,请在该服务的清单中指定进程名称。

来自进程和线程开发人员指南

每种类型的组件元素的清单条目 — <activity>, <service>, <receiver>, and <provider>— 支持一个 android:process 属性,该属性可以指定该组件应在其中运行的进程。您可以设置此属性,以便每个组件在其自己的进程中运行,或者使某些组件共享一个进程而其他组件不共享。您还可以设置 android:process 以便不同应用程序的组件在同一个进程中运行——前提是这些应用程序共享相同的 Linux 用户 ID 并使用相同的证书进行签名。

Android 可能会决定在某个时候关闭某个进程,此时内存不足并且需要更直接地为用户服务的其他进程。在被杀死的进程中运行的应用程序组件因此被破坏。当这些组件再次有工作要做时,将再次为这些组件启动一个流程。

<service>清单文件中

安卓:进程

运行服务的进程的名称。通常,应用程序的所有组件都在为应用程序创建的默认进程中运行。它与应用程序包同名。元素的 process 属性可以为所有组件设置不同的默认值。但是组件可以使用自己的进程属性覆盖默认值,从而允许您将应用程序分布在多个进程中。

如果分配给此属性的名称以冒号 (':') 开头,则会在需要时创建应用程序专用的新进程,并且服务在该进程中运行。如果进程名称以小写字符开头,则服务将在该名称的全局进程中运行,前提是它有权这样做。这允许不同应用程序中的组件共享一个进程,从而减少资源使用。

不知道为什么提到这一点的其他答案被否决。我过去使用过这种方法,而今天,我创建了一个简单的 Activity 应用程序,该应用程序在不同的进程上提供了一个服务,以确保我没有发疯。我使用 Android Device Monitor 来终止应用程序的进程。您可以在 ADM 中看到这两个单独的进程,并且可以看到当应用程序的进程被终止时,服务的进程不会。

于 2016-08-26T15:18:49.977 回答
1

Start not sticky 在 kitkat 之上不起作用,而另一个 onTaskRemoved 在 Marshmellow 之上不起作用。onTaskRemoved 可以通过处理一些异常来使用。没有工作。但是试试那个。

于 2017-03-04T07:56:54.747 回答
0

如果你使用 IntentService,它有一个

onHandleIntent() 

方法,您应该在其中放置需要执行的代码。它在一个单独的线程中执行(不是您的应用程序运行的 UI 线程),因此您的应用程序不应影响它。代码执行完毕后,线程终止,服务自动停止。

于 2013-03-16T18:27:20.710 回答
0

我遇到了同样的问题,并且能够通过使服务在全局进程中运行来解决它。您可以通过将以下内容添加到清单标记来执行此操作:

进程="com.myapp.ProcessName"

(随便起个名字。)

当我这样做时,我发现当应用程序从列表中滑出时,我的服务没有被终止(并重新启动)。大概这是因为当你刷掉它时应用程序进程被杀死,但全局服务进程没有。

这样做的缺点是您的应用程序和服务之间的通信现在必须通过 IBinder 接口;您不能直接从另一个应用程序或服务中调用函数,因为它们在不同的进程中运行。

于 2015-04-01T15:50:54.810 回答
0

我知道回答这个问题已经很晚了,但可能对其他人有帮助。这真的帮助了我的音乐播放器应用程序。

如果有一些服务可能会破坏或影响用户体验,如音乐等,那么在这种情况下,您必须使用通知,当服务启动成功时,然后创建通知并使用该功能

startForeground(int Notification_id,Notification);

这将在后台运行您的服务,而无需重新启动和重新调用其方法

https://developer.android.com/reference/android/app/Service.html

于 2016-09-28T16:27:22.383 回答