首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么我的安卓服务在进程终止时重启,即使我使用的是START_NOT_STICKY?

为什么我的安卓服务在进程终止时重启,即使我使用的是START_NOT_STICKY?
EN

Stack Overflow用户
提问于 2012-09-29 07:06:51
回答 3查看 37.3K关注 0票数 56

我的应用程序使用了一种模式,即我使用Context#startService()启动服务,同时使用Context#bindService()绑定到它。这是为了让我可以独立于当前是否有任何客户端绑定到服务来控制服务的生命周期。然而,我最近注意到,每当我的应用程序被系统终止时,它很快就会重新启动任何正在运行的服务。在这一点上,服务将永远不会被告知停止,这将导致电池耗尽,无论何时发生。下面是一个最小的例子:

我找到了一个有类似问题的人here,但它从来没有被诊断或解决。

服务:

代码语言:javascript
复制
@Override
public void onCreate() {
    Toast.makeText(this, "onCreate", Toast.LENGTH_LONG).show();
}

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

@Override
public IBinder onBind(Intent intent) {
    return new Binder();
}

活动:

代码语言:javascript
复制
@Override
protected void onStart() {
    super.onStart();
    Intent service = new Intent(this, BoundService.class);
    startService(service);
    bindService(service, mServiceConnection, 0);
}

@Override
protected void onStop() {
    unbindService(mServiceConnection);
    Toast.makeText(this, "unbindService", Toast.LENGTH_SHORT).show();
    super.onStop();
}

为了测试它,我启动了应用程序,它启动了服务并绑定到它。然后我退出了应用程序,这会解除绑定(但让服务继续运行)。然后我就这么做了

代码语言:javascript
复制
$ adb shell am kill com.tavianator.servicerestart

果不其然,5秒后,出现了"onCreate“祝酒词,表示服务再次启动。Logcat显示了以下内容:

代码语言:javascript
复制
$ adb logcat | grep BoundService
W/ActivityManager(  306): Scheduling restart of crashed service com.tavianator.servicerestart/.BoundService in 5000ms
I/ActivityManager(  306): Start proc com.tavianator.servicerestart for service com.tavianator.servicerestart/.BoundService: pid=20900 uid=10096 gids={1028}

如果我用BIND_AUTO_CREATE替换startService()模式,问题就不会发生(即使我在应用程序仍然绑定到服务时使其崩溃)。如果我从不绑定到服务,它也可以工作。但是start、bind和unbind的组合似乎永远不会让我的服务停止。

在杀死应用程序之前使用dumpsys会显示以下内容:

代码语言:javascript
复制
$ adb shell dumpsys activity services com.tavianator.servicerestart
ACTIVITY MANAGER SERVICES (dumpsys activity services)
  Active services:
  * ServiceRecord{43099410 com.tavianator.servicerestart/.BoundService}
    intent={cmp=com.tavianator.servicerestart/.BoundService}
    packageName=com.tavianator.servicerestart
    processName=com.tavianator.servicerestart
    baseDir=/data/app/com.tavianator.servicerestart-2.apk
    dataDir=/data/data/com.tavianator.servicerestart
    app=ProcessRecord{424fb5c8 20473:com.tavianator.servicerestart/u0a96}
    createTime=-20s825ms lastActivity=-20s825ms
    executingStart=-5s0ms restartTime=-20s825ms
    startRequested=true stopIfKilled=true callStart=true lastStartId=1
    Bindings:
    * IntentBindRecord{42e5e7c0}:
      intent={cmp=com.tavianator.servicerestart/.BoundService}
      binder=android.os.BinderProxy@42aee778
      requested=true received=true hasBound=false doRebind=false
EN

回答 3

Stack Overflow用户

发布于 2012-10-02 20:37:57

查看此document部分:服务生命周期更改(从1.6开始)

已更新

经过一些调查(创建了项目,一步一步地运行所描述的命令等),我发现代码完全按照“服务生命周期更改”中的描述工作。

我发现:

adb shell am kill com.tavianator.servicerestart杀死nothing

(尝试adb shell,然后在shell am kill com.tavianator.servicerestart

您将面临error消息Error: Unknown command: kill)

所以,

运行你的应用程序

运行adb shell

在shell中运行ps命令

查找应用程序的PID编号

在shell运行命令kill <app_xx_PID>

你的PID号在哪里?

对服务重复终止步骤(如果它在自己的进程中运行)

检查服务是否正在运行(不应该),并在5-7秒后重新启动

更新

一种解决方案(不够好,但在某些情况下可用)是stopSelf(),例如:

代码语言:javascript
复制
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
    stopSelf();
    return START_NOT_STICKY;
}

更新

更新的解决方案

代码语言:javascript
复制
void writeState(int state) {
    Editor editor = getSharedPreferences("serviceStart", MODE_MULTI_PROCESS)
            .edit();
    editor.clear();
    editor.putInt("normalStart", state);
    editor.commit();
}

int getState() {
    return getApplicationContext().getSharedPreferences("serviceStart",
            MODE_MULTI_PROCESS).getInt("normalStart", 1);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (getState() == 0) {
        writeState(1);
        stopSelf();
    } else {
        writeState(0);
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
    }
    return START_NOT_STICKY;
}

当进程被终止时,为什么服务受到限制?

根据这个document

当服务启动时,它有一个独立于启动它的组件的生命周期,服务可以在后台无限运行,即使启动它的组件被销毁。因此,当服务的工作完成时,应该通过调用stopSelf()来停止它自己,或者其他组件可以通过调用stopService().来停止它

Caution:重要的是,您的应用程序在完成工作后停止其服务,以避免浪费系统资源和消耗电池电量。如果需要,其他组件可以通过调用stopService()来停止服务。即使您为服务启用了绑定,如果服务收到对onStartCommand()的调用,您也必须自己停止该服务

另一份文件上写道:

*START_NOT_STICKY* -如果在onStartCommand()返回后系统终止了服务,则不要重新创建服务,除非有待交付的意图。这是最安全的选择,可以避免在不必要的情况下运行您的服务,并且当您的应用程序可以简单地重新启动任何未完成的作业时。

所以,在阅读了这篇文档和一些实验后,我认为系统会将手动终止的服务视为未完成(crashed:@see W/ActivityManager(306): Scheduling restart of crashed service)并重新启动它,尽管onStartCommand返回了这个值。

stopSelf()或stopService() - no重启,如果作业完成,为什么不重启呢?

票数 31
EN

Stack Overflow用户

发布于 2012-10-05 04:59:36

我认为这里的问题是,对于通过调用startService()启动的服务,其绑定的内部记帐(受对bindService()/unbindService()的调用的影响)在某种程度上阻止了服务在被计划在进程终止时重新启动后正确关闭。

根据您的偏好,似乎有三种方法可以避免这个问题(您已经在问题中提到了前两种方法):

  • 仅使用startService()/stopService(),根本不使用bindService()/unbindService()
  • 仅使用bindService()/D12(使用D13),根本不使用D14/D15。如果您需要D18/D19和E221,请覆盖服务中的C24方法并从中调用D25:

@Override public boolean onUnbind( intent ) { stopSelf();super.onUnbind(intent);}

在我的测试中:

使用第一种替代方法的

  • 将在日志中打印以下行:

W/活动管理器( nnn):计划在5000ms中重新启动崩溃的服务xxxx

但在此之后,该服务将不会真正重新启动。

第二个替代方案将导致您的服务在解除绑定后被销毁(在活动的onStop()中,在您的example).

  • The第三个替代方案中,第三个替代方案基本上会使您的代码行为与第二个替代方案相似(无需对其进行太多更改)。

希望这能有所帮助!

票数 10
EN

Stack Overflow用户

发布于 2012-10-08 04:22:27

重新考虑一下你的模式怎么样?

START_NOT_STICKY的旗帜出现在安卓发展过程中的某个时刻(1.6?之后)。最有可能的是,您在服务生命周期中面临着一些微妙的回归,这是在当时引入的。因此,即使现在找到了一个同样微妙的解决方案-并被神奇地验证可以在所有可能的设备上工作-它也不一定会在新的Android版本上工作。

无论如何,这种生命周期模式看起来很不寻常,这可能就是您遇到这个奇怪问题的原因。

让我们考虑两个案例。

  1. Normal (?)。某些活动会启动服务。不久之后,它被绑定、使用、解除绑定--然后呢?不知何故停止了?为什么这款case?
  2. Abnormal.没有明显的电池消耗?服务在某些地方被终止(随机?)点。当重新启动时,它实现了一些错误的假设,并保持活动,做一些耗尽电池的事情-

这一切看起来都很奇怪。

我会分析你的产品中的并发进程。都是重要的案子。一些图表等。最有可能的结果是,对这种模式的需求将被消除。

否则,任何解决方法看起来都将是一个脆弱的黑客攻击。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12648447

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档