导致崩溃的后台服务

时间:2017-06-29 08:51:08

标签: android android-intent android-service

我的问题可能是对服务的误解和我对它们的使用,或者可能与其他应用程序发生冲突。当我开始一项特定的活动时,我会启动两个后台服务 - 位置跟踪以提供行进距离和经过的计时器,这两个服务都通过BroadcastReceiver传递给活动。我使用Long通过我的主要Intent中的Activity对象发起每项服务:

if (!Utils.isServiceRunning(this, TrackService.class)) {
    Intent i = new Intent(this, TrackService.class);
    i.putExtra("SUB_ID", submissionID);
    startService(i);
}

我使用以下代码来检测服务是否已在运行:

public static boolean isServiceRunning(Activity activity, Class<?> serviceClass) {
    ActivityManager manager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (serviceClass.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

onStartCommand的示例如下:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    submissionID = intent.getLongExtra("SUB_ID", 0L);
    // I then use this submissionID to get other data

    super.onStartCommand(intent, flags, startId);
    return START_STICKY;
}

在某些设备上,这似乎完美无缺 - 服务在后台运行,允许我在应用程序中使用其他活动。也可以放入和退出应用程序,当我下次打开我的特定活动时,距离和时间会更新并更正。

然而,我收到了大量的崩溃报告,用户的轶事证据显示,在我的应用程序退出使用相机之后(我还没有确定我的应用程序在使用其他应用程序时是否发生了崩溃) ,或当他们重新进入我的应用程序时):

Fatal Exception: java.lang.RuntimeException: Unable to start service edu.cornell.birds.ebird.services.TrackService@3f77db78 with null: java.lang.NullPointerException: Attempt to invoke virtual method 'long android.content.Intent.getLongExtra(java.lang.String, long)' on a null object reference
   at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3149)
   at android.app.ActivityThread.access$2500(ActivityThread.java:165)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1515)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5669)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'long android.content.Intent.getLongExtra(java.lang.String, long)' on a null object reference
   at edu.cornell.birds.ebird.services.TrackService.onStartCommand(TrackService.java:102)
   at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3112)
   at android.app.ActivityThread.access$2500(ActivityThread.java:165)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1515)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5669)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

在我看来,由于某种原因服务正在停止,并且重新启动时无法访问意图中的数据(我理解NullPointer错误是什么)。这是因为我正在返回START_STICKY,它会在重启时返回空数据吗?

有谁知道该服务应该停止的任何原因?我怎么能防止这种情况发生?

我无法使用我自己的设备(Moto G4,Android 7.0)重新创建,它可以像我希望的那样工作。

4 个答案:

答案 0 :(得分:1)

Android中的任何内容都可以在任何时候被杀死,例如,如果系统资源很少。因此,您的代码应始终为此做好准备。 O中将处理后台服务处理方式的重大变化。 这个,我想你可以阅读Android documentation

&#34;如果此服务尚未运行,它将被实例化并启动(如果需要,为其创建一个进程);如果它正在运行,那么它仍然在运行。对此方法的每次调用都将导致对目标服务的onStartCommand(Intent,int,int)方法的相应调用,其意图在此处给出。&#34;

所以不要对调用者负责,而是将其移动到被调用者。每当您需要传递一些信息时启动服务,然后让服务(如果它已经实例化并且正在运行)知道该服务。此外,防范空值,就像在Android中一样:

// caller, no if-check here (no responsibility)
startService(new Intent(this, TrackService.class).putExtra("SUB_ID", submissionID));

然后另一方,被叫服务:

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    if (intent == null) {
        // do nothing and return
        return START_STICKY;
    }

    // here your intent is not null, use it
    submissionID = intent.getLongExtra("SUB_ID", 0L);
    // if you got the most recent data already, do nothing
    // else, retrieve data using the passed id

    return START_STICKY;

最后但同样重要的是,请注意您无需调用 super.onStartCommand(intent,flags,startId); ,查看其实现。 请记住责任倒置,因为它是一种更通用的方法,您可以使用相当多的Android编码,不仅仅是在这个例子中。希望它有所帮助!

答案 1 :(得分:1)

Android可以(并且会)随时停止Service。由于您已从START_STICKY返回onStartCommand(),因此Android应在Service被杀后重新启动。在这种情况下,重启后您将在Intent中获得空onStartCommand()。如果想要,则无法阻止Android杀死您的Service

您需要定期将任何有用的数据保存在持久存储(SharedPreferences,文件,数据库等)中,以便在重新启动后可以恢复。

答案 2 :(得分:0)

此问题的快速解决方法是:

在服务中的onStartCommand()回调中返回START_REDELIVER_INTENT而不是START_STICKY,以便在重新启动后发送整个意图。

你可以用on条件包含onStartCommand中的代码:

if (null == intent || null == intent.getAction ()) {


        return START_STICKY;
}else{
  //ur code goes in

   return START_STICKY;
 }

此空指针异常的原因是: &#34;如果没有任何挂起的启动命令要传递给服务,它将使用空的intent对象调用,因此您必须注意检查这一点。&#34;

答案 3 :(得分:0)

您是否在清单文件的意图操作中注册了广播接收器?还是dynamically