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

Tav*_*nes 53 android android-service

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

我在这里找到了一个有类似问题的人,但它从未被诊断或解决过.

服务:

@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();
}
Run Code Online (Sandbox Code Playgroud)

活动:

@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();
}
Run Code Online (Sandbox Code Playgroud)

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

$ adb shell am kill com.tavianator.servicerestart
Run Code Online (Sandbox Code Playgroud)

果然,5秒钟后,"onCreate"吐司出现,表明该服务再次启动.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}
Run Code Online (Sandbox Code Playgroud)

如果我用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
Run Code Online (Sandbox Code Playgroud)

ra.*_*ra. 29

请看本文档部分:服务生命周期的变化(自1.6起)

更新

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

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

所以,
运行你的应用程序, 在shell运行命令中
运行adb shell
在shell运行ps命令中
查找你的app的PID号你
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;
}
Run Code Online (Sandbox Code Playgroud)


更新
更新解决方案

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;
}
Run Code Online (Sandbox Code Playgroud)

为什么服务在进程被杀死时被重新设置?

根据这份文件:

当服务启动时,它的生命周期独立于启动它的组件,并且服务可以无限期地在后台运行,即使启动它的组件被销毁.因此,当通过调用stopSelf()完成其工作时,服务应该自行停止,或者另一个组件可以通过调用stopService()来停止它.
注意:应用程序在完成工作后停止服务非常重要,以避免浪费系统资源和消耗电池电量.如有必要,其他组件可以通过调用stopService()来停止服务.即使您为服务启用了绑定,如果它曾接到onStartCommand()的调用,您也必须自己停止服务.

从另一方面的文件说:

*START_NOT_STICKY* - 如果系统在onStartCommand()返回后终止服务,则不要重新创建服务,除非有待传递的待处理意图.这是最安全的选项,可以在不需要时以及应用程序只需重新启动任何未完成的作业时避免运行服务.

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


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


Joe*_*Joe 10

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

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

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

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

  • 如果需要startService()/ stopService() bindService()/unbindService(),请覆盖onUnbind()服务中的方法并stopSelf()从中调用:

    @Override
    public boolean onUnbind(Intent intent) {
        stopSelf();
        return super.onUnbind(intent);
    }
    
    Run Code Online (Sandbox Code Playgroud)

从我的测试:

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

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

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

  • 解除绑定后,第二种方法将导致您的服务被销毁(onStop()在您的示例中的Activity 中).

  • 第三种选择基本上会使你的代码表现得像第二种选择(无需更改它).

希望这可以帮助!