2017-06-29 87 views
2

我的问题可能是服务的误解和我对它们的使用,或者与其他应用程序的位置冲突。当我开始一个特定的活动时,我开始了两个后台服务 - 定位跟踪以提供距离,以及已经过的计时器,这两个服务都通过BroadcastReceiver传递给活动。我开始通过Intent对象与Long每个服务从我小学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; 
} 

在某些设备上,这似乎完美工作 - 服务(S)工作ks在后台,允许我在应用程序中使用其他活动&也可以进出应用程序,当我下次打开我的特定活动时,距离和时间会更新并更正。

但是,我收到很多崩溃报告,并且用户的传闻证据表明它在甩掉我的应用程序以使用相机之后(我尚未确定在使用其他应用程序时是否发生了崩溃应用程序,或者当他们重新进入我的应用程序):

Fatal Exception: java.lang.RuntimeException: Unable to start service [email protected] 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) 

在我看来,由于某种原因该服务被停止和重新启动不能从目的访问数据时(我的理解一个空指针错误是什么)。这是因为我返回START_STICKY它在重新启动时返回空数据?

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

我无法重新使用我自己的设备(Moto G4,Android 7.0),它的工作原理与我所希望的一样。

+0

你的'TrackService'类中有一个NullPointer在行号** 102 ** – SripadRaj

+0

可能重复[什么是NullPointerException,我该如何解决它?](https://stackoverflow.com/questions/218384)/what-is-a-nullpointerexception-and-how-do-i-fix-it) – SripadRaj

+0

我的主要问题是“为什么服务应该停止”,这导致了NullPointer(我知道它是什么)。 – iaindownie

回答

1

Android可以(并且会)在任何时候停止您的Service。由于您已经从onStartCommand()返回START_STICKY,因此Android在重新启动Service后应该会被终止。在这种情况下,在重新启动后,您将在onStartCommand()中收到空值Intent。如果想要,Android无法阻止您的Service

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

+0

简洁的解释,谢谢。 – iaindownie

0

对于这个问题快速解决方法是:

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

您可以用if条件类似这样的包装内onStartCommand代码:

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


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

    return START_STICKY; 
} 

之所以有这样的空指针异常将是: “如果没有任何未决的START命令被传送到服务,它将被调用一个空意图对象,所以你必须小心检查这一点。“

+0

谢谢。将使用START_REDELIVER_INTENT不会将位置跟踪和已用时间重新设置为起点吗?如果发生这种情况,那么数据将无用,因为我需要继续这些数据。我更关心试图阻止它重新启动,为什么它重新启动。 – iaindownie

+0

它重新开始呼吁你这样做,通过调用START_STICKY –

+0

好,道歉不超清,重新措词:为什么服务停止? – iaindownie

1

Android中的任何内容都可能随时被杀死,例如系统资源很少。所以你的代码应该随时准备好。而在后台服务的处理方式会在O. 这给予了很大的变化,我想你可以阅读Android documentation

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

所以不要将所有责任保留给调用者,但把它移动到被调用的地方,每当你需要传递一些信息时启动该服务,然后让该服务知道它是否被实例化并运行,以处理该情况。

// 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,而不仅仅是在此示例中。希望能帮助到你!

0

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

+0

动态。只有服务在清单中注册。 – iaindownie

+1

然后检查您是否在* OnResume *和* OnPause *上注册和取消注册接收器,并且检查[Alessio's]的答案[https://stackoverflow.com/users/4469112/alessio] –