Background services crashing

My problem may be a gross misunderstanding of services and their use, as well as a conflict with other applications. When I start a specific action, I start two help services - a location tracking to give the distance traveled, and an expired timer, both of which are passed to the action using BroadcastReceiver

. I start each service using Long

via an object Intent

from my main Activity

:

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

      

And I am using the following code to determine if the service is running:

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;
}

      

Below is an example 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;
}

      

On some devices this works great - the services (services) run in the background, which allows me to use other activities in the app, as well as enter and exit the app, and when I open my specific activity, the distance and time are updated and corrected.

However, I get a lot of crash reports and some data from users shows that after exiting my app, use the camera (I haven't figured out yet if my app crashed while using another app, or when they re-enter my app ):

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)

      

It seems to me that for some reason the service stops and when restarting cannot access the data from intent (I understand what a NullPointer error is). Is it because I am returning START_STICKY

that returns null data on reload?

Does anyone know of any reason why the service should stop? And how can I prevent this?

I cannot recreate with my device (Moto G4, Android 7.0) where it works as I hoped.

+3


source to share


4 answers


Android can (and will) stop yours Service

whenever it wants to. Since you got back START_STICKY

from onStartCommand()

, Android should restart Service

after it dies. In this case, after reboot, you will get zero Intent

in onStartCommand()

. There is no way to prevent Android from killing yours Service

if it wants to.



You need to regularly save any useful data to persistent storage (SharedPreferences, file, database, etc.) so that you can restore it after a restart.

+1


source


Anything in Android can be killed at any time, for example, if the system has few resources. Therefore, your code should always be ready for this. And a big change in how background services are handled will arrive in O. This is given, I think you could read the documentation :

"If this service is not already started, it will be created and started (if necessary, create a process for it), if it is started, it will be started. Each call to this method will result in a corresponding call to the target method onStartCommand (Intent, int, int) with the intent indicated here. "

so don't take full responsibility to the caller, but move it to the callee. Start the service anytime you need to pass some information to it, and then let the service, which already knows if it was created and started, handle the case. Also, protect against nulls like you always do in Android:

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

      



then the other side called the service:

    @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;

      

Last but not least, please note that you don't need to call super.onStartCommand (intent, flags, startId); take a look at its implementation. Keep the inversion of responsibility in mind, because this is a more general approach that you can use quite a lot of Android coding beyond this example. Hope this helps!

+1


source


Quick fix for this issue:

Return START_REDELIVER_INTENT

to the onStartCommand () callback in the service instead START_STICKY

so that the whole intent is dispatched after restart.

or

you can wrap your code inside onStartCommand with an if condition like this:

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


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

   return START_STICKY;
 }

      

The reason for throwing this exception is: "If no pending start commands are sent to the service, they will be called with intent zero, so you must take care of that."

0


source


Have you registered the broadcast receiver in the action of the manifest file? or dynamically ?

0


source







All Articles