web-dev-qa-db-fra.com

Qui a tué mon service?

J'ai lu à peu près toutes les réponses Stackoverflow disponibles sur ce sujet, mais aucune d'entre elles n'a fonctionné.

Objectif: maintenir mon service 24h/24 et 7j/7j

Problème: chaque fois que mon appareil est en mode veille pendant au moins une heure, le service est désactivé


Ce que j'ai essayé de résoudre:

  • Renvoyer START_STICKY depuis onStartCommand() et utiliser startForeground()

        public int onStartCommand(Intent intent, int flags, int startId) {
    
            notification = makeStickyNotification(); //I've simplified the irrelevant code, obviously this would be a real notification I build
    
            startForeground(1234, notification);
            return START_STICKY;
            }
    

Cela fonctionne correctement et redémarre même mon service lorsque le périphérique manque de mémoire, mais cela ne suffit pas pour résoudre le problème qui se produit lorsque mon périphérique se met en veille pendant un certain temps.

  • Utiliser Alarm Manager dans onCreate() de mon activité et dans onStartCommand() de mon service pour appeler un récepteur de diffusion qui appelle mon service

            Intent ll24 = new Intent(this, AlarmReceiver.class);
    
            PendingIntent recurringLl24 = PendingIntent.getBroadcast(this, 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
    
            AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    
            alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000*60, recurringLl24); // Every minute
    

Cela permet de garder mon service actif, mais encore une fois, cela ne résout pas mon problème

  • Utiliser Schedule Task Executor pour le garder en vie

     if (scheduleTaskExecutor == null) {
    
            scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
    
            scheduleTaskExecutor.scheduleAtFixedRate(new mainTask(), 0, 1, TimeUnit.SECONDS);
    
        }
    ...    
    class mainTask implements Runnable {
    
    public void run() {
        // 1 Second Timer
       }
    }
    

    Cela maintient également le service actif, mais ne le maintient pas en vie après un long sommeil.

  • Manifeste de tâche séparée

    Android:launchMode="singleTop"

Cela n'a rien fait

Comment puis-je (1) tester ce problème sans avoir à mettre mon téléphone en veille et à vérifier toutes les heures et (2) à assurer le bon fonctionnement de mon service malgré le périphérique en veille?

6
Ruchir Baronia

Le mystère du meurtre a été résolu, et je sais ce qui a tué mon service. Voici ce que j'ai fait:

  1. Après avoir réalisé que startsticky, startforeground, alarmmanager, scheduleTaskExecutor, et même wakelock étaient incapables de sauver mon service, j'ai réalisé que l'assassin ne pouvait pas être le système Android, car j'avais pris toutes les mesures possibles pour empêcher le système de tuer mon service et il serait toujours tué.  
  2. J'ai réalisé que je devais rechercher un autre suspect, car le service ne mourait pas à cause du système. Pour cela, je devais mener une enquête. J'ai lancé la commande suivante:

    adb Shell dumpsys activity processes > tmp.txt

Cela me donnerait un journal détaillé de tous les processus en cours d'exécution et de leurs priorités système. tmp.txt serait essentiellement le détective dans ce mystère de meurtre.

  1. J'ai parcouru le dossier avec beaucoup de détails. Il semblait que le système donnait la priorité à mon service:

    Proc #31: adj=prcp /FS trm= 0 2205:servicename.service/uID (fg-service)

La ligne ci-dessus indique la priorité exacte d'un processus en cours d'exécution sur le périphérique Android. adj=prcp signifie que le service est un service de premier plan visible. 

À ce stade, je me suis rendu compte que mon service doit rencontrer une erreur quelques heures après avoir exécuté, alors je le laisse courir et mourir. Après sa mort, j’ai produit à nouveau une dumpsys pour examiner l’erreur:

  1. À ce stade, mon service n'était pas répertorié en tant que tâche dans le fichier tmp.txt. Excité, j'ai fait défiler jusqu'au dumpsys et résolu le mystère!

com.curlybrace.ruchir.appName.MyService $ 2.onForeground (MyService.Java:199) sur com.rvalerio.fgchecker.AppChecker $ 2.de courir (AppChecker.Java:118) sur Android. os.Handler.handleCallback (Handler.Java:751) à Android.os.Handler.dispatchMessage (Handler.Java:95) à Android.os.Looper.loop (Looper.Java: 154) Sur Android.app.ActivityThread.main (ActivityThread.Java:6123) Sur Java.lang.reflect.Method.invoke (Méthode native) Sur com.Android .internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.Java:867) sur com.Android.internal.os.ZygoteInit.main (ZygoteInit.Java:757)

La trace de la pile qui a tué mon service était affichée à cet endroit! Essentiellement, une variable qui vérifierait l'application utilisée au premier plan deviendrait nulle après quelques heures d'inactivité, ce qui provoquerait une exception et tuerait le service!

À retenir: Si votre service est tué et que vous avez fait tout votre possible pour vous assurer qu'il ne devrait pas être tué, effectuez une dumpsys et examinez la gravité de l'activité de votre appareil. processus. Je vous garantis que vous trouverez le problème de cette façon.

Je voudrais toujours avoir la prime attribuée à @Khemraj car sa réponse pourrait être une excellente solution pour quelqu'un qui n'a pas démarré son service correctement. Cependant, j'accepte cette réponse car c'est la solution qui a réellement résolu le problème.

6
Ruchir Baronia

Votre service a été tué par mode Doze ou veille d'Android. Cela a été introduit dans Android 6.0 (API de niveau 23).

Somnolence restrictions

Les restrictions suivantes s'appliquent à vos applications en Doze:

  • L'accès au réseau est suspendu. 
  • Le système ignore les verrous de réveil. 
  • Les alarmes standard AlarmManagery compris setExact() et setWindow()) sont différées à la fenêtre de maintenance suivante. 
  • Si vous devez définir des alarmes qui se déclenchent à Doze, utilisez setAndAllowWhileIdle() ou setExactAndAllowWhileIdle()
  • Les alarmes définies avec setAlarmClock() continuent à se déclencher normalement - le système quitte Doze peu de temps avant le déclenchement de ces alarmes. 
  • Le système n’effectue pas d’analyses Wi-Fi. 
  • Le système ne permet pas aux adaptateurs de synchronisation de s'exécuter. Le système n'autorise pas JobScheduler à s'exécuter.

_ (Le système a donc ignoré votre Alarm Clocks, Scheduler, etc.

Dans Android Oreo _, version Android définie limites aux services d'arrière-plan .

Pour améliorer l'expérience utilisateur, Android 8.0 (API niveau 26) impose Des limites à ce que les applications peuvent faire lorsqu'elles s'exécutent en arrière-plan.

Néanmoins, si l'application doit toujours exécuter son service, nous pouvons créer un service de premier plan.

Limitations du service en arrière-plan:Lorsqu'une application est inactive, il existe des limites À son utilisation des services en arrière-plan. Ceci ne s'applique pas aux services au premier plan , Qui sont plus perceptibles par l'utilisateur.

Créez donc un service de premier plan. Dans lequel vous placerez un notification pour l'utilisateurpendant que votre service est en cours d'exécution. Voir cette réponse (Il y en a beaucoup d'autres)

Et maintenant, si vous ne voulez pas de notification pour votre service. Une solution est pour ça.

Vous pouvez créer une tâche périodique qui démarrera votre service. Le service fera son travail et s’arrêtera lui-même. De ce fait, votre application ne sera pas considérée comme une batterie épuisante.

Vous pouvez créer une tâche périodique avec Alarm Manager , Planificateur de tâches , Evernote-Jobs ou Work Manager

  • Au lieu de dire le pour et le contre de chacun. Je te dis juste mieux. Le gestionnaire de travaux est préférablesolution pour les tâches périodiques. Ce qui a été introduit avec Android Architecture Component
  • Contrairement à Job-Scheduler (uniquement> 21 API), il fonctionnera pour toutes les versions.
  • En outre, il commence à fonctionner après un mode veille
  • Faites un Android Boot Receiver pour le service de planification après l’amorçage du périphérique.

J'ai créé un service permanent avec Work-Manager, qui fonctionne parfaitement.

15
Khemraj

onDestroy () n'est vraiment pas fiable et ne sera pas appelé souvent. Idem pour onLowMemory () callbacks. Il n'y a aucun moyen de prendre un rappel garanti si Android décide de tuer votre processus ou si l'utilisateur décide de forcer Arrêter votre application.

Il est normal que le périphérique utilisateur passe en mode veille, votre service meurt. Lisez à propos de wakelocks . Essayez quelque chose comme ça à votre service: In manifest:

<uses-permission Android:name="Android.permission.WAKE_LOCK" />

En service: 

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    "tag");
wakeLock.acquire();

Mais c’est vraiment difficile pour l’utilisateur et totalement anti-modèle dans le monde Android, à cause de la consommation de la batterie.

Une autre option consiste à déclencher un service comme toutes les 10 minutes. Définissez l'intention en attente sur WakefulBroadcastReceiver (où vous pouvez démarrer votre service) et planifiez-la avec le gestionnaire d'alarmes avec l'indicateur RTC_WAKE_UP

1
HeyAlex

Le mode assoupi tue les services pour économiser la batterie. La seule solution valable pour vous consiste à créer un service de premier plan dans Oreo et au-dessus. https://developer.Android.com/about/versions/oreo/background

1
Muhammad Muzammil

À partir du SDK 26, un service doit avoir son "MainActivity" relative au premier plan OR ce service doit être démarré comme au premier plan à l'aide de "startForegroundService ()". Le "startForeground ()" ne fonctionne pas comme prévu si le SDK cible est 26+ mais besoin de l'autre manière que je viens d'expliquer.

Après cela, vous pouvez utiliser le code suivant pour Kill et redémarrer l'application à partir de zéro (oui, même le service est tué de cette manière):

Intent mStartActivity = new Intent(context, StartActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
1
emandt