Фоновые службы, вызывающие сбой

Моя проблема может быть в недопонимании сервисов и их использовании или в возможном конфликте с другими приложениями. Когда я запускаю определенное действие, я запускаю две фоновые службы - отслеживание местоположения для определения пройденного расстояния и истекший таймер, которые передаются в действие с 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 ответа

Решение

Android может (и будет) остановить ваш Service когда захочет. Потому что вы вернулись START_STICKY от onStartCommand()Android должен перезагрузить ваш Service после того как он был убит. В этом случае вы получите ноль Intent в onStartCommand() после перезагрузки. Нет способа предотвратить убийство Android Service если захочет.

Вам необходимо регулярно сохранять любые полезные данные в постоянном хранилище (SharedPreferences, файл, база данных и т. Д.), Чтобы вы могли восстанавливаться после перезапуска.

Все в Android может быть убито в любой момент, например, если в системе мало ресурсов. Так что ваш код должен быть всегда готов к этому. И в O произойдет большое изменение в том, как обрабатываются фоновые сервисы. Учитывая это, я думаю, вы могли бы прочитать документацию по Android:

"Если этот сервис еще не запущен, он будет создан и запущен (при необходимости создаст для него процесс); если он работает, он останется запущенным. Каждый вызов этого метода приведет к соответствующему вызову onStartCommand целевой службы (Intent, int, int), с указанным здесь намерением."

так что не несите всю ответственность за звонящего, а переносите его на вызываемого. Запускайте сервис каждый раз, когда вам нужно передать ему некоторую информацию, а затем разрешите сервису, который уже знает, запущен ли он и запущен, для обработки дела. Также, защититесь от нулей, как всегда в 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(намерение, флаги, startId);, посмотрите на его реализацию. Имейте в виду инверсию ответственности, потому что это более общий подход, вы можете использовать довольно много кодирования Android, не только в этом примере. Надеюсь, поможет!

Быстрое решение этой проблемы:

Вернуть START_REDELIVER_INTENT в обратном вызове onStartCommand() в службе вместо START_STICKY так что все намерение отправляется после перезапуска.

или же

вы можете обернуть код внутри onStartCommand условием if, например:

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


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

   return START_STICKY;
 }

Причиной этого исключения нулевого указателя будет: "если нет ожидающих команд запуска, которые должны быть доставлены в службу, она будет вызываться с объектом с нулевым намерением, поэтому вы должны позаботиться об этом".

Вы зарегистрировали своего вещательного приемника в умышленном действии файла манифеста? или динамически?

Другие вопросы по тегам