我的问题可能是对服务的误解和我对它们的使用,或者可能与其他应用程序发生冲突。当我开始一项特定的活动时,我会启动两个后台服务 - 位置跟踪以提供行进距离和经过的计时器,这两个服务都通过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)重新创建,它可以像我希望的那样工作。
答案 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?