为什么我的Android服务会在进程被终止时重新启动,即使我使用了START_NOT_STICKY?

时间:2012-09-28 23:06:51

标签: android android-service

我的应用使用的格式是我使用Context#startService()启动服务,并使用Context#bindService()绑定它。这样我就可以独立于任何客户端当前是否绑定它来控制服务的生命周期。但是,我最近注意到,每当我的应用程序被系统杀死时,它很快就会重新启动所有正在运行的服务。此时,服务永远不会被告知停止,这会导致电池耗尽。这是一个最小的例子:

我发现有类似问题的人here,但它从未被诊断或解决过。

服务:

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

的活动:

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

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

$ adb shell am kill com.tavianator.servicerestart

果然,5秒钟后,“onCreate”toast出现,表示该服务再次启动。 Logcat显示了这个:

$ 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显示:

$ 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

3 个答案:

答案 0 :(得分:29)

请查看此document部分:服务生命周期更改(自1.6起)

更新

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

中的描述工作

我找到的内容:
adb shell am kill com.tavianator.servicerestart杀死没有
(尝试adb shell,然后在shell am kill com.tavianator.servicerestart中你将面临错误消息Error: Unknown command: kill

所以,
运行你的申请,
运行adb shell
在shell中运行ps命令
找到您的应用程序的PID号
在shell运行命令kill <app_xx_PID>
你的PID编号在哪里
重复杀死服务的步骤(如果它在自己的进程中运行)
检查服务是否正在运行(不应该),5-7秒后重新启动
更新 一种解决方案(不够好,但在某些情况下可用)是stopSelf()例如:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
    stopSelf();
    return START_NOT_STICKY;
}


更新
更新的解决方案

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()
警告:重要的是你的   应用程序在完成工作后停止服务,以避免   浪费系统资源和消耗电池电量。如有必要,   其他组件可以通过调用stopService()来停止服务。甚至   如果为服务启用绑定,则必须始终停止   为自己服务如果它曾调用 onStartCommand()

来自其他手工文件说:

  

* START_NOT_STICKY * - 如果系统在onStartCommand()返回后终止服务,请不要重新创建服务,除非那里   正在等待交付的意图。这是最安全的选择   在没有必要时和您的应用程序可以运行时运行您的服务   只需重新启动任何未完成的工作。

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


stopSelf()或stopService() - 无重启,为什么不完成工作?

答案 1 :(得分:10)

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

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

  • 仅使用startService() / stopService(),根本不要使用bindService() / unbindService()

  • 仅使用bindService() / unbindService()(使用BIND_AUTO_CREATE),根本不要使用startService() / stopService()

    < / LI>
  • 如果您需要startService() / stopService() bindService() / unbindService(),请覆盖您的onUnbind()方法服务并从中致电stopSelf()

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

从我的测试开始:

  • 使用第一个替代方法将在日志中打印此行:

      

    W / ActivityManager(nnn):在5000ms内调度崩溃服务xxxx的重启

    但之后服务将不会重新启动。

  • 第二种方法会导致您的服务在解除绑定后被销毁(在您的示例中的活动onStop()中)。

  • 第三种选择基本上会使你的代码表现得像第二种选择(不需要太多改变)。

希望这有帮助!

答案 2 :(得分:2)

如何重新考虑模式

START_NOT_STICKY标志在Android演变期间的某个时刻出现(1.6之后?)。最有可能的是,您正面临着当时引入的服务生命周期中的一些微妙回归。因此,即使现在找到一个同样微妙的解决方案 - 并且神奇地验证可以在所有可能的设备上运行 - 它也不一定适用于新的Android版本。

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

让我们考虑两种情况。

  1. 正常(?)。某些活动启动该服务。不久之后它就会受到束缚,使用,不受约束 - 然后是什么?不知何故停了?为什么在这种情况下没有明显的电池消耗?

  2. 异常。服务在一些(随机?)点死亡。重新启动时,它实现了一些错误的假设并保持活动状态,做了一些耗尽电池的事情?

  3. 这一切看起来都很奇怪。

    我会分析您产品中的并发流程。所有重大案件。一些图表,等等。因此,很可能会消除对这种模式的需求。

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