当应用程序被杀死时,Android后台服务会重新启动

38

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

startService(new Intent(this, MyService.class));
我创建了一个服务,这样即使应用程序被销毁,后台服务仍然会继续收集数据。我尝试了这种方法,并且在某种程度上它起到了作用。我的问题是,当我杀死应用程序时,服务似乎会重新启动,因为onCreate()服务和onStart()方法被调用。请问是否有任何方法可以避免服务重启? 更新: 正如下面的回答中建议的那样,我在服务中添加了以下方法,但没有运气。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_NOT_STICKY;
}

我也遇到了这个问题。而且我也没有绑定任何东西。 - fersarr
1
你没有提到它,所以:你尝试在AndroidManifest中指定一个单独的全局进程来运行你的服务了吗? - Stevie
你找到任何解决方案了吗?如果有,请指导我。 - H Raval
我最终使用了前台服务。这种类型的服务被视为用户积极意识到的进程,因此当系统内存不足时,它不会被视为可杀死的候选进程。为了确保用户知道正在运行的进程,必须提供通知,只有在停止服务时才能解除。 - duncanportelli
6个回答

20

这取决于onStartCommand返回的值。

你必须返回START_NOT_STICKY。

根据文档

对于已启动的服务,它们可以决定运行在两种额外的主要操作模式中,具体取决于它们从onStartCommand()方法返回的值:对于需要随时显式启动和停止的服务,使用START_STICKY;而对于只应该在处理发送到它们的任何命令时保持运行的服务,则使用START_NOT_STICKY或 START_REDELIVER_INTENT。

简而言之: 如果返回START_STICKY,当资源可用时,服务将被重新创建。如果返回START_NOT_STICKY,则必须通过发送新的intent来重新激活服务。

由于所有这些引起了我的好奇心,所以我制作了一个示例应用程序来测试这个问题。你可以在这里找到包含所有源代码的zip文件。 有一个startService按钮和一个stopService按钮,它们做你预期的事情。 该服务在onStartCommand中返回START_NOT_STICKY。 我在onCreate、onStartCommand和onDestroy中放置了toasts。

以下是发生的情况:

  • 如果我点击start,则会调用onCreate和onStart。
  • 如果我点击stop,则触发onDestroy。
  • 如果我点击两次start,则只调用一次onCreate和两次onStartCommand。

因此,它表现得像人们预期的那样。

如果我启动服务并按照你所描述的那样杀死应用程序,则不会调用onDestroy,但也不会调用onCreate或onStart。

如果我回到应用程序并再次点击start,则会调用onCreate,这意味着,如我之前所写,START_NOT_STICKY可以防止服务自动重新启动。

我猜你的应用程序中还有其他东西会重新启动服务(可能是一个待处理的intent)。


1
你如何“终止”应用程序?以及你如何知道onCreate()和onStart()被反复调用? - fedepaol
2
жҲ‘йҖҡиҝҮеңЁвҖңжңҖиҝ‘дҪҝз”Ёзҡ„еә”з”ЁзЁӢеәҸвҖқжүҳзӣҳдёӯеҗ‘е·Ұж»‘еҠЁеә”з”ЁзЁӢеәҸжқҘе…ій—ӯеә”з”ЁзЁӢеәҸпјҲжҲ‘дҪҝз”ЁиҝҗиЎҢAndroid 4.1.1зҡ„Galaxy NexusпјүгҖӮжҲ‘зҹҘйҒ“onCreate()е’ҢonStart()дјҡиў«еҶҚж¬Ўи°ғз”ЁпјҢеӣ дёәжҲ‘еңЁиҝҷдёӨз§Қж–№жі•зҡ„ејҖеӨҙжҳҫзӨәдәҶдёҖдёӘtoastйҖҡзҹҘгҖӮ - duncanportelli
你是否将活动绑定到服务? - fedepaol
2
感谢您的申请。当我杀死您发送给我的应用程序时,尽管onDestroy()没有被调用,但服务并不会继续运行,而是结束了。我可以从任务管理器中检查到这一点。当我将服务更改为START_STICKY,并杀死应用程序时,服务会重新启动。我想我必须接受这个事实。 - duncanportelli
START_NOT_STICKY适用于短暂的服务(您不介意立即关闭它),如上面引用的文档所述。即使它以某种方式避免了这个问题,对于长时间运行的后台服务来说,这似乎不是一个明智的解决方案。(我原本认为它可能会阻止Android重新启动服务,但不能阻止其首先被杀死。) - sam
显示剩余2条评论

5
该应用和服务运行在同一进程中,这意味着当应用程序被杀死时,您的服务也会被杀死。更改onStartCommand的返回值不会影响此进程。它只是告诉服务在您告诉它或完成所需操作后启动/停止。如您在原始帖子的评论中提到的,将其设置为前台进程可以解决问题,但这实际上只是强制服务具有高优先级,并不能真正解决问题。
要更改服务以使其单独终止,并假定由于使用onStartCommand而不是绑定服务,因此指定该服务在清单中的进程名称即可。
进程和线程开发人员指南中了解更多信息:
每种组件元素的清单条目—<activity>, <service>, <receiver>, and <provider>—都支持一个android:process属性,可以指定该组件应在其中运行的进程。您可以设置此属性,使每个组件在自己的进程中运行,也可以使一些组件共享一个进程,而其他组件不共享。您还可以设置android:process,以便不同应用程序的组件在同一进程中运行,前提是这些应用程序共享相同的Linux用户ID并使用相同的证书签名。
当内存不足并且其他立即为用户服务的进程需要时,Android可能会决定关闭某个进程。因此,在被杀死的进程中运行的应用程序组件将被销毁。当它们再次有工作要做时,进程会为这些组件重新启动。

来自Manifest文件中的<service>:

android:process

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

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

不确定为什么提到这一点的其他答案被投票否决了。我过去曾使用过这种方法,今天创建了一个简单的只有一个活动应用程序,其中服务在不同的进程上,只是为了确保我没有疯狂。我使用 Android 设备监视器来杀死应用程序的进程。您可以在 ADM 中看到两个单独的进程,并且可以看到当应用程序的进程被杀死时,服务的进程没有被杀死。


1

在KitKat及以上版本中,启动不粘性不起作用,而另一个onTaskRemoved在Marshmallow及以上版本中也不起作用。onTaskRemoved可用于处理某些异常情况,但我没有解决这个问题。不过可以尝试一下。


0

我知道回答这个问题已经晚了很多,但也许对其他人有帮助。这确实帮助了我开发我的音乐播放器应用。

如果有一些可能会影响用户体验的服务,比如音乐等,那么在这种情况下,你必须使用通知,并且当服务成功启动时,创建通知并使用这个函数。

startForeground(int Notification_id,Notification);

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

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


0
如果您正在使用IntentService,它具有一个


onHandleIntent() 

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


3
我认为他指的不是IntentService(它具有onHandleIntent方法),而是普通的Service。 - fedepaol
抱歉,我是新手。是的,我指的是“普通服务”。 - duncanportelli

0

我遇到了同样的问题,并通过使服务在全局进程中运行来解决它。您可以通过将以下内容添加到清单标签中来实现此目的:

process="com.myapp.ProcessName"

(随意想一个名字)

当我这样做的时候,我发现当应用从列表中划掉时,我的服务没有被杀死(和重新启动)。可能是因为当你把它划掉时,应用程序进程被杀死了,但全局服务进程没有。

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


这对我似乎不起作用,至少在Android 5.1.1上不行。 - Nicu Surdu

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